From c72bce8533913c70d121c0798f4c6d07ce78537a Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Wed, 24 Jun 2026 17:31:44 +0530 Subject: [PATCH 001/144] ci(release): add signed release artifacts with SLSA provenance (#2470) --- .../vocabularies/TraceMachina/accept.txt | 2 + .github/workflows/release.yaml | 210 ++++++++++++++++++ CONTRIBUTING.md | 33 +++ typos.toml | 2 + 4 files changed, 247 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/styles/config/vocabularies/TraceMachina/accept.txt b/.github/styles/config/vocabularies/TraceMachina/accept.txt index b852843d9..351743ec7 100644 --- a/.github/styles/config/vocabularies/TraceMachina/accept.txt +++ b/.github/styles/config/vocabularies/TraceMachina/accept.txt @@ -6,6 +6,8 @@ Astro Bazel Bazelisk [Bb]oolean +[Kk]eyless +Sigstore Cloudflare Colab composable diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 000000000..58d389f6c --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,210 @@ +--- +# Builds the NativeLink binaries for every supported target, signs each release +# asset with keyless Sigstore cosign, generates an SBOM, and produces SLSA Build +# Level 3 provenance that is attached to the GitHub Release. +# +# This is what the OpenSSF Scorecard "Signed-Releases" check inspects: it reads +# the GitHub Releases API and looks for a cryptographic signature (`*.sig`) plus +# SLSA provenance (`*.intoto.jsonl`) among the release assets. The cosign image +# signatures in GHCR are NOT visible to that check, which is why container-only +# signing left the score at zero. +name: Signed release artifacts + +on: + # Fires automatically the moment a maintainer clicks "Publish release". + release: + types: [published] + # Manual entry point for (re)building and signing assets on an existing + # release tag, e.g. to backfill releases that predate this workflow. + workflow_dispatch: + inputs: + tag: + description: 'Existing release tag to build, sign and attach assets to (e.g. v1.5.2).' + required: true + type: string + +permissions: read-all + +concurrency: + group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag }} + cancel-in-progress: false + +jobs: + setup: + name: Resolve release tag + runs-on: ubuntu-24.04 + timeout-minutes: 5 + outputs: + tag: ${{ steps.resolve.outputs.tag }} + version: ${{ steps.resolve.outputs.version }} + steps: + - name: Resolve tag and version + id: resolve + run: | + set -euo pipefail + TAG="${{ github.event.release.tag_name || inputs.tag }}" + if [[ -z "${TAG}" ]]; then + echo "::error::No release tag could be determined." + exit 1 + fi + echo "tag=${TAG}" >> "${GITHUB_OUTPUT}" + echo "version=${TAG#v}" >> "${GITHUB_OUTPUT}" + + build: + name: Build ${{ matrix.target }} + needs: setup + permissions: + contents: write # upload assets to the release + id-token: write # keyless cosign signing via Sigstore + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-musl + os: ubuntu-24.04 + attr: nativelink-x86_64-linux + - target: aarch64-unknown-linux-musl + os: ubuntu-24.04 + attr: nativelink-aarch64-linux + - target: aarch64-apple-darwin + os: macos-26 + attr: nativelink-aarch64-darwin + # macos-26-intel is the x86_64 macOS runner; drop this entry if Intel + # macOS runners are retired and x86_64 macOS binaries aren't required. + - target: x86_64-apple-darwin + os: macos-26-intel + attr: nativelink-x86_64-darwin + runs-on: ${{ matrix.os }} + timeout-minutes: 90 + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Checkout ${{ needs.setup.outputs.tag }} + uses: >- # v6.0.2 + actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ needs.setup.outputs.tag }} + persist-credentials: false + + - name: Prepare Worker + uses: ./.github/actions/prepare-nix + with: + nativelink_attic_token: ${{ secrets.NATIVELINK_ATTIC_TOKEN }} + + - name: Install Cosign + uses: >- # v4.1.2 + sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 + + - name: Build static binary + run: | + nix build --fallback -L ".#${{ matrix.attr }}" + + - name: Package artifact + id: package + run: | + set -euo pipefail + VERSION="${{ needs.setup.outputs.version }}" + ASSET="nativelink-${VERSION}-${{ matrix.target }}" + + # macOS runners ship `shasum` rather than coreutils `sha256sum`; both + # emit the " " format the SLSA generator expects. + sha256() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" + else + shasum -a 256 "$1" + fi + } + + STAGE="$(mktemp -d)" + install -m 0755 result/bin/nativelink "${STAGE}/nativelink" + install -m 0644 LICENSE "${STAGE}/LICENSE" + install -m 0644 README.md "${STAGE}/README.md" + tar -C "${STAGE}" -czf "${ASSET}.tar.gz" nativelink LICENSE README.md + + sha256 "${ASSET}.tar.gz" | tee "${ASSET}.tar.gz.sha256" + echo "asset=${ASSET}" >> "${GITHUB_OUTPUT}" + + - name: Generate SBOM + uses: >- # v0.24.0 + anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 + with: + file: result/bin/nativelink + format: spdx-json + output-file: ${{ steps.package.outputs.asset }}.spdx.json + upload-artifact: false + upload-release-assets: false + + - name: Sign artifact and SBOM (keyless) + run: | + set -euo pipefail + ASSET="${{ steps.package.outputs.asset }}" + for f in "${ASSET}.tar.gz" "${ASSET}.spdx.json"; do + cosign sign-blob --yes \ + --output-signature "${f}.sig" \ + --output-certificate "${f}.pem" \ + "${f}" + done + + - name: Upload assets to release + run: | + set -euo pipefail + ASSET="${{ steps.package.outputs.asset }}" + gh release upload "${{ needs.setup.outputs.tag }}" \ + "${ASSET}.tar.gz" \ + "${ASSET}.tar.gz.sig" \ + "${ASSET}.tar.gz.pem" \ + "${ASSET}.spdx.json" \ + "${ASSET}.spdx.json.sig" \ + "${ASSET}.spdx.json.pem" \ + --clobber + + - name: Upload hash for provenance + uses: >- # v4.6.0 + actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 + with: + name: sha256-${{ matrix.target }} + path: ${{ steps.package.outputs.asset }}.tar.gz.sha256 + if-no-files-found: error + retention-days: 5 + + combine-hashes: + name: Combine artifact hashes + needs: build + runs-on: ubuntu-24.04 + timeout-minutes: 5 + outputs: + subjects: ${{ steps.hashes.outputs.subjects }} + steps: + - name: Download hashes + uses: >- # v4.3.0 + actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 + with: + pattern: sha256-* + path: hashes + merge-multiple: true + + - name: Combine into base64 subjects + id: hashes + run: | + set -euo pipefail + cat hashes/*.sha256 > combined.sha256 + echo "subjects=$(base64 -w0 < combined.sha256)" >> "${GITHUB_OUTPUT}" + + provenance: + name: Generate SLSA provenance + needs: [setup, combine-hashes] + permissions: + actions: read # read the workflow run to build provenance + id-token: write # keyless signing of the provenance + contents: write # attach provenance to the release + # The SLSA generator MUST be referenced by an immutable semver tag, not a + # commit SHA: slsa-verifier validates the reusable-workflow ref to confirm a + # trusted builder produced the provenance, so a SHA pin would fail + # verification. This is the one intentional exception to SHA-pinning here. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 + with: + base64-subjects: ${{ needs.combine-hashes.outputs.subjects }} + upload-assets: true + upload-tag-name: ${{ needs.setup.outputs.tag }} + provenance-name: nativelink-${{ needs.setup.outputs.version }}.intoto.jsonl diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48b691c8f..283059764 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -476,6 +476,39 @@ most automatically generated changelogs provide. 10. Once all notes are in line, click `Publish Release`. +11. Publishing the release triggers the + [`Signed release artifacts`](.github/workflows/release.yaml) workflow. It + builds the `nativelink` binary for every supported target, signs each asset + with keyless Sigstore cosign, generates an SBOM, and produces SLSA Build + Level 3 provenance, attaching all of it to the GitHub Release. These signed + assets are what the OpenSSF Scorecard `Signed-Releases` check inspects — the + cosign signatures on the GHCR container images are not visible to that check. + + Wait for the workflow to finish, then confirm the release page lists, for + each target, a `*.tar.gz` plus its `*.sig`/`*.pem`, an `*.spdx.json` SBOM, + and a `*.intoto.jsonl` provenance file. You can verify any asset locally: + + ```bash + # Verify the SLSA provenance covers the artifact. + slsa-verifier verify-artifact nativelink-0.x.y-x86_64-unknown-linux-musl.tar.gz \ + --provenance-path nativelink-0.x.y.intoto.jsonl \ + --source-uri github.com/TraceMachina/nativelink \ + --source-tag v0.x.y + + # Verify the cosign signature. + cosign verify-blob nativelink-0.x.y-x86_64-unknown-linux-musl.tar.gz \ + --signature nativelink-0.x.y-x86_64-unknown-linux-musl.tar.gz.sig \ + --certificate nativelink-0.x.y-x86_64-unknown-linux-musl.tar.gz.pem \ + --certificate-identity-regexp '^https://github.com/TraceMachina/nativelink/' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com + ``` + + If a release ever ships without signed assets (for example a release created + before this workflow existed), re-run the workflow manually against the tag: + `Actions → Signed release artifacts → Run workflow`, entering the tag (e.g. + `v0.x.y`). It will rebuild, re-sign, and attach the assets to that existing + release. + ## Conduct NativeLink Code of Conduct is available in the diff --git a/typos.toml b/typos.toml index 356f7e4ba..d6efd4c10 100644 --- a/typos.toml +++ b/typos.toml @@ -1,6 +1,8 @@ [default.extend-words] # `conly_flags` in lre-cc conly = "conly" +# in-toto attestation format used by SLSA provenance (`*.intoto.jsonl`) +intoto = "intoto" [default] # Old wrong spelling support From f170cdf41326ae9b24e5c0d1b2b344fdd165b4b6 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Wed, 24 Jun 2026 16:57:18 +0100 Subject: [PATCH 002/144] Default --fallback for nix rather than having to set it everywhere (#2475) --- .github/actions/prepare-nix/action.yaml | 6 +++++- .github/actions/test-and-upload-image/action.yaml | 12 ++++++------ .github/workflows/coverage.yaml | 2 +- .github/workflows/lre.yaml | 4 ++-- .github/workflows/nix.yaml | 12 ++++++------ .github/workflows/web.yaml | 2 +- templates/bazel/.github/workflows/lre.yaml | 2 +- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/.github/actions/prepare-nix/action.yaml b/.github/actions/prepare-nix/action.yaml index 8c0532852..2ab5262b5 100644 --- a/.github/actions/prepare-nix/action.yaml +++ b/.github/actions/prepare-nix/action.yaml @@ -15,6 +15,10 @@ runs: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 with: source-tag: v3.13.0 + extra-conf: | + fallback = true # So we always work even if the cache is down + log-lines = 100 # Necessary in CI to get enough failure context + keep-outputs = true # Don't delete non-root outputs, because might be needed for other builds - name: Setup attic cache uses: >- # v4 @@ -37,7 +41,7 @@ runs: # * https://github.com/zhaofengli/attic/issues/226 # * https://github.com/zhaofengli/attic/pull/317 # We use the original one first to get caching of this - nix build --fallback github:TraceMachina/attic + nix build github:TraceMachina/attic RUST_BACKTRACE=full RUST_LOG=debug,hyper_util=info,rustls=info ./result/bin/attic watch-store nativelink &> attic-push.log & # For the cases where we don't have a cache of it, push it diff --git a/.github/actions/test-and-upload-image/action.yaml b/.github/actions/test-and-upload-image/action.yaml index 8c0cb01a5..072f38698 100644 --- a/.github/actions/test-and-upload-image/action.yaml +++ b/.github/actions/test-and-upload-image/action.yaml @@ -24,8 +24,8 @@ runs: - name: Test multi-arch image if: ${{ inputs.multi-arch == 'true' && inputs.testing }} run: | - nix run --fallback .#create-multi-arch-image localhost:5000 ${{ inputs.image }}:${{ inputs.tag }} ${{ inputs.components }} - nix run --fallback .#local-image-test localhost:5000/${{ inputs.image }}:${{ inputs.tag }} + nix run .#create-multi-arch-image localhost:5000 ${{ inputs.image }}:${{ inputs.tag }} ${{ inputs.components }} + nix run .#local-image-test localhost:5000/${{ inputs.image }}:${{ inputs.tag }} shell: bash -euo pipefail {0} # FIXME: after we've merged this and made sure it all works _then_ remove the 2457 ref @@ -33,8 +33,8 @@ runs: - name: Upload multi-arch image if: ${{ inputs.multi-arch == 'true' && contains(fromJson('["refs/heads/main", "refs/pull/2457/merge"]'), github.ref) }} run: | - nix run --fallback .#regctl-ghcr-login - nix run --fallback .#create-multi-arch-image ghcr.io ${{ github.repository_owner }}/${{ inputs.image }}:${{ inputs.tag }} ${{ inputs.components }} + nix run .#regctl-ghcr-login + nix run .#create-multi-arch-image ghcr.io ${{ github.repository_owner }}/${{ inputs.image }}:${{ inputs.tag }} ${{ inputs.components }} env: # FIXME: When https://github.com/github/roadmap/issues/558 gets fixed, replace with a fine-grained token GHCR_USERNAME: ${{ inputs.GHCR_USERNAME }} @@ -46,13 +46,13 @@ runs: - name: Test image if: ${{ inputs.multi-arch == 'false' && inputs.testing }} run: | - nix run --fallback .#local-image-test ${{ inputs.image }} + nix run .#local-image-test ${{ inputs.image }} shell: bash -euo pipefail {0} - name: Upload image if: ${{ inputs.multi-arch == 'false' && contains(fromJson('["refs/heads/main", "refs/pull/2457/merge"]'), github.ref) }} run: | - nix run --fallback .#publish-ghcr ${{ inputs.image }} + nix run .#publish-ghcr ${{ inputs.image }} env: GHCR_REGISTRY: ghcr.io/${{ github.repository_owner }} GHCR_USERNAME: ${{ inputs.GHCR_USERNAME }} diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index d65605b5a..fdf52eeeb 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -41,7 +41,7 @@ jobs: - name: Generate coverage run: | - nix build --fallback -L .#nativelinkCoverageForHost + nix build -L .#nativelinkCoverageForHost cat result/text/index.txt - name: Upload coverage artifact diff --git a/.github/workflows/lre.yaml b/.github/workflows/lre.yaml index 16bf40f66..61927eca8 100644 --- a/.github/workflows/lre.yaml +++ b/.github/workflows/lre.yaml @@ -46,7 +46,7 @@ jobs: env: TOOLCHAIN: ${{ matrix.toolchain }} run: > - nix develop --impure --fallback --command + nix develop --impure --command bash -c "bazel run \ --lockfile_mode=error \ --verbose_failures \ @@ -88,7 +88,7 @@ jobs: # COMMIT: ${{ github.event.pull_request.head.sha || github.sha }} # TOOLCHAIN: ${{ matrix.toolchain }} # run: | -# nix develop --fallback --impure --command bash -c 'cat > kustomization.yaml << EOF +# nix develop --impure --command bash -c 'cat > kustomization.yaml << EOF # apiVersion: kustomize.config.k8s.io/v1beta1 # kind: Kustomization # resources: diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 774adfb1c..009061aec 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -46,7 +46,7 @@ jobs: # so a flaky GitHub fetch aborts the build with no retry. We retry the download in this case. delay=5 for attempt in 1 2 3; do - if nix develop --fallback --impure --command \ + if nix develop --impure --command \ bazel test //... \ --verbose_failures \ --lockfile_mode=error \ @@ -92,7 +92,7 @@ jobs: - name: Invoke Cargo build in Nix shell run: > - nix develop --fallback --impure --command + nix develop --impure --command bash -c "cargo test --all --profile=smol" - name: Teardown Worker @@ -123,7 +123,7 @@ jobs: - name: Test nix run run: | - nix run --fallback -L .#nativelink-is-executable-test + nix run -L .#nativelink-is-executable-test - name: Teardown Worker uses: ./.github/actions/end-nix @@ -151,7 +151,7 @@ jobs: - name: Test ${{ matrix.test-name }} run run: | - nix run --fallback -L .#${{ matrix.test-name }}-with-nativelink-test + nix run -L .#${{ matrix.test-name }}-with-nativelink-test - name: Teardown Worker uses: ./.github/actions/end-nix @@ -177,7 +177,7 @@ jobs: - name: Get commands list run: | - COMMANDS=$(nix run --fallback .#rbe-toolchain-with-nativelink-test list) + COMMANDS=$(nix run .#rbe-toolchain-with-nativelink-test list) echo "matrix={\"command\":${COMMANDS}}" >> $GITHUB_OUTPUT id: set-matrix @@ -200,7 +200,7 @@ jobs: - name: Test ${{ matrix.command }} with rbe run: | - nix run --fallback -L .#rbe-toolchain-with-nativelink-test ${{ matrix.command }} + nix run -L .#rbe-toolchain-with-nativelink-test ${{ matrix.command }} - name: Teardown Worker uses: ./.github/actions/end-nix diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index 50dd2898d..289ebd3ce 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -51,7 +51,7 @@ jobs: if: github.event_name == 'pull_request' working-directory: web run: | - nix develop --fallback --impure --command bash -c " + nix develop --impure --command bash -c " bun install && bun run build " diff --git a/templates/bazel/.github/workflows/lre.yaml b/templates/bazel/.github/workflows/lre.yaml index cb0a24a59..ef5e4eca2 100644 --- a/templates/bazel/.github/workflows/lre.yaml +++ b/templates/bazel/.github/workflows/lre.yaml @@ -34,4 +34,4 @@ jobs: source-tag: v3.13.0 - name: Build project - run: nix develop --fallback --command bazel build ... + run: nix develop --command bazel build ... From 2cd0507b85efd6663bb574f49654e489a9175ea7 Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Thu, 25 Jun 2026 04:08:28 +0530 Subject: [PATCH 003/144] ci(release): keep legacy cosign signature/cert outputs (#2478) Recent cosign defaults --new-bundle-format on, which ignores --output-signature/--output-certificate and instead requires --bundle. With no --bundle passed it tries to write to an empty path and fails ("create bundle file: open : no such file or directory"), breaking the signing step. Pass --new-bundle-format=false so the split .sig/.pem files the release assets and verifiers rely on are produced as before. --- .github/workflows/release.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 58d389f6c..3a41ca754 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -140,7 +140,12 @@ jobs: set -euo pipefail ASSET="${{ steps.package.outputs.asset }}" for f in "${ASSET}.tar.gz" "${ASSET}.spdx.json"; do + # Newer cosign defaults --new-bundle-format on, which ignores the + # --output-signature/--output-certificate flags and writes a single + # bundle instead. Keep the legacy split .sig/.pem outputs the + # release assets and verifiers expect. cosign sign-blob --yes \ + --new-bundle-format=false \ --output-signature "${f}.sig" \ --output-certificate "${f}.pem" \ "${f}" From ae5762e5412cede276c37e1f33f6f92788e68dfd Mon Sep 17 00:00:00 2001 From: corcillo Date: Wed, 24 Jun 2026 17:37:05 -0700 Subject: [PATCH 004/144] deps: bump quinn-proto to 0.11.15 and memmap2 to 0.9.11 (RustSec) (#2479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch-level, lockfile-only bumps clearing two recently published RustSec advisories against transitive dependencies: * quinn-proto 0.11.14 -> 0.11.15 — RUSTSEC-2026-0185, remote memory exhaustion via unbounded out-of-order QUIC stream reassembly. * memmap2 0.9.9 -> 0.9.11 — RUSTSEC-2026-0186, unchecked pointer offset. Both are semver-compatible patch releases; `cargo check --workspace` passes. Restores the OpenSSF Scorecard Vulnerabilities check (these two advisories took it from 5 to 3, overall 9.4 to 9.2). Co-authored-by: Marcus Eagan --- Cargo.lock | 8 ++++---- MODULE.bazel.lock | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1cc2205b9..2882eb11d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2873,9 +2873,9 @@ checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memmap2" -version = "0.9.9" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -4042,9 +4042,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" dependencies = [ "bytes", "getrandom 0.3.4", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 05b9e99cf..74cd70646 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1063,7 +1063,7 @@ "md-5_0.10.6": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"md5-asm\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(any(target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"}],\"features\":{\"asm\":[\"md5-asm\"],\"default\":[\"std\"],\"force-soft\":[],\"loongarch64_asm\":[],\"oid\":[\"digest/oid\"],\"std\":[\"digest/std\"]}}", "memchr_2.8.0": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"libc\":[],\"logging\":[\"dep:log\"],\"rustc-dep-of-std\":[\"core\"],\"std\":[\"alloc\"],\"use_std\":[\"std\"]}}", "memchr_2.8.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"libc\":[],\"logging\":[\"dep:log\"],\"rustc-dep-of-std\":[\"core\"],\"std\":[\"alloc\"],\"use_std\":[\"std\"]}}", - "memmap2_0.9.9": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.151\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"owning_ref\",\"req\":\"^0.4.1\"},{\"name\":\"stable_deref_trait\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{}}", + "memmap2_0.9.11": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.151\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"owning_ref\",\"req\":\"^0.4.1\"},{\"name\":\"stable_deref_trait\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{}}", "memory-stats_1.2.0": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"android\\\", target_os = \\\"macos\\\", target_os = \\\"ios\\\", target_os = \\\"freebsd\\\"))\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_System\",\"Win32_System_ProcessStatus\",\"Win32_System_Threading\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"always_use_statm\":[]}}", "mimalloc_0.1.48": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libmimalloc-sys\",\"req\":\"^0.1.44\"}],\"features\":{\"debug\":[\"libmimalloc-sys/debug\"],\"debug_in_debug\":[\"libmimalloc-sys/debug_in_debug\"],\"default\":[],\"extended\":[\"libmimalloc-sys/extended\"],\"local_dynamic_tls\":[\"libmimalloc-sys/local_dynamic_tls\"],\"no_thp\":[\"libmimalloc-sys/no_thp\"],\"override\":[\"libmimalloc-sys/override\"],\"secure\":[\"libmimalloc-sys/secure\"],\"v3\":[\"libmimalloc-sys/v3\"]}}", "mime_0.3.17": "{\"dependencies\":[],\"features\":{}}", @@ -1157,7 +1157,7 @@ "pyo3-macros_0.28.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"pyo3-macros-backend\",\"req\":\"=0.28.3\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"experimental-async\":[\"pyo3-macros-backend/experimental-async\"],\"experimental-inspect\":[\"pyo3-macros-backend/experimental-inspect\"],\"multiple-pymethods\":[]}}", "pyo3_0.28.3": "{\"dependencies\":[{\"name\":\"anyhow\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"assert_approx_eq\",\"req\":\"^1.1.0\"},{\"name\":\"bigdecimal\",\"optional\":true,\"req\":\"^0.4.7\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.25\"},{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.25\"},{\"default_features\":false,\"name\":\"chrono-tz\",\"optional\":true,\"req\":\">=0.10, <0.11\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\">=0.10, <0.11\"},{\"name\":\"either\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"eyre\",\"optional\":true,\"req\":\">=0.6.8, <0.7\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\">=0.15.0, <0.17\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\">=2.5.0, <3\"},{\"name\":\"inventory\",\"optional\":true,\"req\":\"^0.3.5\"},{\"name\":\"jiff-02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"name\":\"libc\",\"req\":\"^0.2.62\"},{\"name\":\"lock_api\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.4\"},{\"name\":\"num-complex\",\"optional\":true,\"req\":\">=0.4.6, <0.5\"},{\"name\":\"num-rational\",\"optional\":true,\"req\":\"^0.4.1\"},{\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.16\"},{\"name\":\"once_cell\",\"req\":\"^1.21\"},{\"default_features\":false,\"name\":\"ordered-float\",\"optional\":true,\"req\":\"^5.0.0\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12\"},{\"features\":[\"arc_lock\"],\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.3\"},{\"name\":\"portable-atomic\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_has_atomic = \\\"64\\\"))\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.0\"},{\"features\":[\"resolve-config\"],\"kind\":\"build\",\"name\":\"pyo3-build-config\",\"req\":\"=0.28.3\"},{\"name\":\"pyo3-ffi\",\"req\":\"=0.28.3\"},{\"name\":\"pyo3-macros\",\"optional\":true,\"req\":\"=0.28.3\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.6.1\"},{\"default_features\":false,\"name\":\"rust_decimal\",\"optional\":true,\"req\":\"^1.15\"},{\"kind\":\"dev\",\"name\":\"send_wrapper\",\"req\":\"^0.6\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.61\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.12.0\"},{\"default_features\":false,\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.38\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\">=1.0.115\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.12.0\"},{\"features\":[\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.10.0\"}],\"features\":{\"abi3\":[\"pyo3-build-config/abi3\",\"pyo3-ffi/abi3\"],\"abi3-py310\":[\"abi3-py311\",\"pyo3-build-config/abi3-py310\",\"pyo3-ffi/abi3-py310\"],\"abi3-py311\":[\"abi3-py312\",\"pyo3-build-config/abi3-py311\",\"pyo3-ffi/abi3-py311\"],\"abi3-py312\":[\"abi3-py313\",\"pyo3-build-config/abi3-py312\",\"pyo3-ffi/abi3-py312\"],\"abi3-py313\":[\"abi3-py314\",\"pyo3-build-config/abi3-py313\",\"pyo3-ffi/abi3-py313\"],\"abi3-py314\":[\"abi3\",\"pyo3-build-config/abi3-py314\",\"pyo3-ffi/abi3-py314\"],\"abi3-py37\":[\"abi3-py38\",\"pyo3-build-config/abi3-py37\",\"pyo3-ffi/abi3-py37\"],\"abi3-py38\":[\"abi3-py39\",\"pyo3-build-config/abi3-py38\",\"pyo3-ffi/abi3-py38\"],\"abi3-py39\":[\"abi3-py310\",\"pyo3-build-config/abi3-py39\",\"pyo3-ffi/abi3-py39\"],\"arc_lock\":[\"lock_api\",\"lock_api/arc_lock\",\"parking_lot?/arc_lock\"],\"auto-initialize\":[],\"bigdecimal\":[\"dep:bigdecimal\",\"num-bigint\"],\"chrono-local\":[\"chrono/clock\",\"dep:iana-time-zone\"],\"default\":[\"macros\"],\"experimental-async\":[\"macros\",\"pyo3-macros/experimental-async\"],\"experimental-inspect\":[\"pyo3-macros/experimental-inspect\"],\"extension-module\":[\"pyo3-ffi/extension-module\"],\"full\":[\"macros\",\"anyhow\",\"arc_lock\",\"bigdecimal\",\"bytes\",\"chrono\",\"chrono-local\",\"chrono-tz\",\"either\",\"experimental-async\",\"experimental-inspect\",\"eyre\",\"hashbrown\",\"indexmap\",\"jiff-02\",\"lock_api\",\"num-bigint\",\"num-complex\",\"num-rational\",\"ordered-float\",\"parking_lot\",\"py-clone\",\"rust_decimal\",\"serde\",\"smallvec\",\"time\",\"uuid\"],\"generate-import-lib\":[\"pyo3-ffi/generate-import-lib\"],\"macros\":[\"pyo3-macros\"],\"multiple-pymethods\":[\"inventory\",\"pyo3-macros/multiple-pymethods\"],\"nightly\":[],\"num-bigint\":[\"dep:num-bigint\",\"dep:num-traits\"],\"parking_lot\":[\"dep:parking_lot\",\"lock_api\"],\"py-clone\":[]}}", "quick-xml_0.31.0": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"memchr\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\">=1.0.100\"},{\"kind\":\"dev\",\"name\":\"serde-value\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.79\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"}],\"features\":{\"async-tokio\":[\"tokio\"],\"default\":[],\"encoding\":[\"encoding_rs\"],\"escape-html\":[],\"overlapped-lists\":[],\"serde-types\":[\"serde/derive\"],\"serialize\":[\"serde\"]}}", - "quinn-proto_0.11.14": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"fastbloom\",\"optional\":true,\"req\":\"^0.14\"},{\"default_features\":false,\"features\":[\"wasm_js\"],\"name\":\"getrandom\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"lru-slab\",\"req\":\"^0.1.2\"},{\"name\":\"qlog\",\"optional\":true,\"req\":\"^0.15.2\"},{\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"features\":[\"wasm32_unknown_unknown_js\"],\"name\":\"ring\",\"req\":\"^0.17\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"features\":[\"web\"],\"name\":\"rustls-pki-types\",\"req\":\"^1.7\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"slab\",\"req\":\"^0.4.6\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"alloc\",\"alloc\"],\"name\":\"tinyvec\",\"req\":\"^1.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.45\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"__rustls-post-quantum-test\":[],\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"aws-lc-rs?/aws-lc-sys\",\"aws-lc-rs?/prebuilt-nasm\"],\"aws-lc-rs-fips\":[\"aws-lc-rs\",\"aws-lc-rs?/fips\"],\"bloom\":[\"dep:fastbloom\"],\"default\":[\"rustls-ring\",\"log\",\"bloom\"],\"log\":[\"tracing/log\"],\"platform-verifier\":[\"dep:rustls-platform-verifier\"],\"qlog\":[\"dep:qlog\"],\"ring\":[\"dep:ring\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"rustls?/aws-lc-rs\",\"aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"rustls-aws-lc-rs\",\"aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"rustls?/ring\",\"ring\"]}}", + "quinn-proto_0.11.15": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"fastbloom\",\"optional\":true,\"req\":\"^0.14\"},{\"default_features\":false,\"features\":[\"wasm_js\"],\"name\":\"getrandom\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"lru-slab\",\"req\":\"^0.1.2\"},{\"name\":\"qlog\",\"optional\":true,\"req\":\"^0.15.2\"},{\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"features\":[\"wasm32_unknown_unknown_js\"],\"name\":\"ring\",\"req\":\"^0.17\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"features\":[\"web\"],\"name\":\"rustls-pki-types\",\"req\":\"^1.7\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"slab\",\"req\":\"^0.4.6\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"alloc\",\"alloc\"],\"name\":\"tinyvec\",\"req\":\"^1.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.45\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"__rustls-post-quantum-test\":[],\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"aws-lc-rs?/aws-lc-sys\",\"aws-lc-rs?/prebuilt-nasm\"],\"aws-lc-rs-fips\":[\"aws-lc-rs\",\"aws-lc-rs?/fips\"],\"bloom\":[\"dep:fastbloom\"],\"default\":[\"rustls-ring\",\"log\",\"bloom\"],\"log\":[\"tracing/log\"],\"platform-verifier\":[\"dep:rustls-platform-verifier\"],\"qlog\":[\"dep:qlog\"],\"ring\":[\"dep:ring\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"rustls?/aws-lc-rs\",\"aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"rustls-aws-lc-rs\",\"aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"rustls?/ring\",\"ring\"]}}", "quinn-udp_0.5.14": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"libc\",\"req\":\"^0.2.158\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1.19\",\"target\":\"cfg(windows)\"},{\"name\":\"socket2\",\"req\":\">=0.5, <0.7\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"features\":[\"sync\",\"rt\",\"rt-multi-thread\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.10\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_IO\",\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <=0.60\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"tracing\",\"log\"],\"direct-log\":[\"dep:log\"],\"fast-apple-datapath\":[],\"log\":[\"tracing/log\"]}}", "quinn_0.11.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.22\"},{\"name\":\"async-io\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"async-std\",\"optional\":true,\"req\":\"^1.11\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"crc\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"directories-next\",\"req\":\"^2\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.19\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"proto\",\"package\":\"quinn-proto\",\"req\":\"^0.11.12\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"kind\":\"dev\",\"name\":\"rustls-pemfile\",\"req\":\"^2\"},{\"name\":\"smol\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"socket2\",\"req\":\">=0.5, <0.7\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"features\":[\"sync\",\"rt\",\"rt-multi-thread\",\"time\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"std-future\"],\"kind\":\"dev\",\"name\":\"tracing-futures\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"features\":[\"tracing\"],\"name\":\"udp\",\"package\":\"quinn-udp\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"aws-lc-rs\":[\"proto/aws-lc-rs\"],\"aws-lc-rs-fips\":[\"proto/aws-lc-rs-fips\"],\"bloom\":[\"proto/bloom\"],\"default\":[\"log\",\"platform-verifier\",\"runtime-tokio\",\"rustls-ring\",\"bloom\"],\"lock_tracking\":[],\"log\":[\"tracing/log\",\"proto/log\",\"udp/log\"],\"platform-verifier\":[\"proto/platform-verifier\"],\"qlog\":[\"proto/qlog\"],\"ring\":[\"proto/ring\"],\"runtime-async-std\":[\"async-io\",\"async-std\"],\"runtime-smol\":[\"async-io\",\"smol\"],\"runtime-tokio\":[\"tokio/time\",\"tokio/rt\",\"tokio/net\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"aws-lc-rs\",\"proto/rustls-aws-lc-rs\",\"proto/aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"dep:rustls\",\"aws-lc-rs-fips\",\"proto/rustls-aws-lc-rs-fips\",\"proto/aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"ring\",\"proto/rustls-ring\",\"proto/ring\"]}}", "quote_1.0.45": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"proc-macro\"],\"proc-macro\":[\"proc-macro2/proc-macro\"]}}", From 1829fa462e6e766b7ac1e5faf74e862925359d8d Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Thu, 25 Jun 2026 19:17:52 +0530 Subject: [PATCH 005/144] ci(release): pin cosign to v2.5.3 for legacy .sig/.pem output (#2480) The signing step pulls cosign from an unpinned installer, so it now installs v3.x, which forces the new bundle/signing-config format and no longer honors --new-bundle-format=false ("must provide --new-bundle-format or --bundle ... with --signing-config"). That broke release signing. Pin cosign-release to v2.5.3, the newest version that still emits the split signature/certificate files the rest of the workflow uploads and verifiers consume, so the behavior stops drifting with upstream cosign. --- .github/workflows/release.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3a41ca754..0024a1068 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -94,6 +94,11 @@ jobs: - name: Install Cosign uses: >- # v4.1.2 sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 + with: + # Pin cosign: v3.x forces the new bundle format and drops the legacy + # split signature/certificate output the release assets and verifiers + # depend on. v2.5.3 still emits .sig/.pem files. + cosign-release: v2.5.3 - name: Build static binary run: | @@ -140,10 +145,9 @@ jobs: set -euo pipefail ASSET="${{ steps.package.outputs.asset }}" for f in "${ASSET}.tar.gz" "${ASSET}.spdx.json"; do - # Newer cosign defaults --new-bundle-format on, which ignores the - # --output-signature/--output-certificate flags and writes a single - # bundle instead. Keep the legacy split .sig/.pem outputs the - # release assets and verifiers expect. + # Cosign is pinned to v2.5.3 (see the install step) so these flags + # produce the split .sig/.pem files the release assets and verifiers + # expect; --new-bundle-format=false keeps that explicit. cosign sign-blob --yes \ --new-bundle-format=false \ --output-signature "${f}.sig" \ From 452a72035630a08bdd350c2584f210fe8f1cf476 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Thu, 25 Jun 2026 16:12:35 +0100 Subject: [PATCH 006/144] Fix custom image building (#2477) * push not append for JS * Rename image to nativelink * Correct testing support in test-and-upload-image * Enable force-push for test-and-upload --- .github/actions/test-and-upload-image/action.yaml | 11 +++++++---- .github/workflows/custom-image.yaml | 7 ++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/actions/test-and-upload-image/action.yaml b/.github/actions/test-and-upload-image/action.yaml index 072f38698..1bd1aecf8 100644 --- a/.github/actions/test-and-upload-image/action.yaml +++ b/.github/actions/test-and-upload-image/action.yaml @@ -17,12 +17,15 @@ inputs: required: true GHCR_PASSWORD: required: true + force-push: + required: false + default: false runs: using: "composite" steps: # FIXME: merge the multi-arch and single-arch paths - name: Test multi-arch image - if: ${{ inputs.multi-arch == 'true' && inputs.testing }} + if: ${{ inputs.multi-arch == 'true' && inputs.testing == 'true' }} run: | nix run .#create-multi-arch-image localhost:5000 ${{ inputs.image }}:${{ inputs.tag }} ${{ inputs.components }} nix run .#local-image-test localhost:5000/${{ inputs.image }}:${{ inputs.tag }} @@ -31,7 +34,7 @@ runs: # FIXME: after we've merged this and made sure it all works _then_ remove the 2457 ref # See https://github.com/TraceMachina/nativelink/pull/2457 - name: Upload multi-arch image - if: ${{ inputs.multi-arch == 'true' && contains(fromJson('["refs/heads/main", "refs/pull/2457/merge"]'), github.ref) }} + if: ${{ inputs.multi-arch == 'true' && (inputs.force-push == 'true' || contains(fromJson('["refs/heads/main"]'), github.ref)) }} run: | nix run .#regctl-ghcr-login nix run .#create-multi-arch-image ghcr.io ${{ github.repository_owner }}/${{ inputs.image }}:${{ inputs.tag }} ${{ inputs.components }} @@ -44,13 +47,13 @@ runs: shell: bash -euo pipefail {0} - name: Test image - if: ${{ inputs.multi-arch == 'false' && inputs.testing }} + if: ${{ inputs.multi-arch == 'false' && inputs.testing == 'true' }} run: | nix run .#local-image-test ${{ inputs.image }} shell: bash -euo pipefail {0} - name: Upload image - if: ${{ inputs.multi-arch == 'false' && contains(fromJson('["refs/heads/main", "refs/pull/2457/merge"]'), github.ref) }} + if: ${{ inputs.multi-arch == 'false' && (inputs.force-push == 'true' || contains(fromJson('["refs/heads/main"]'), github.ref)) }} run: | nix run .#publish-ghcr ${{ inputs.image }} env: diff --git a/.github/workflows/custom-image.yaml b/.github/workflows/custom-image.yaml index b90c4e909..3de1ce34a 100644 --- a/.github/workflows/custom-image.yaml +++ b/.github/workflows/custom-image.yaml @@ -6,10 +6,10 @@ on: image: description: 'Image to build' required: false - default: 'image' + default: 'nativelink' type: choice options: - - image + - nativelink - nativelink-worker-init - nativelink-worker-lre-cc skip_signing: @@ -50,7 +50,7 @@ jobs: var matrix = JSON.parse(fs.readFileSync('.github/images-matrix.json', 'utf8')); var validImages = []; for (const option of matrix['include']) { - validImages.append(option['image']); + validImages.push(option['image']); } if (context.eventName === 'workflow_dispatch') { core.setOutput('should_build', 'true'); @@ -142,6 +142,7 @@ jobs: tag: ${{ steps.version.outputs.version-string }} GHCR_USERNAME: ${{ vars.GHCR_PUBLISH_USER }} GHCR_PASSWORD: ${{ secrets.GHCR_PUBLISH_TOKEN }} + force-push: true - name: Output image info run: | From 66f7159365cb5009e935d50920d0a5125bc336a8 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Thu, 25 Jun 2026 17:05:28 +0100 Subject: [PATCH 007/144] Improve logging for detect_duplicate_upload and fake redis sync (#2481) * Improve logging for detect_duplicate_upload * Add boot synchronisation for fake redis --- .../src/dynamic_fake_redis.rs | 13 +++++-- nativelink-redis-tester/src/fake_redis.rs | 38 +++++++++++++------ .../src/read_only_redis.rs | 13 +++++-- nativelink-store/src/filesystem_store.rs | 1 + .../tests/filesystem_store_test.rs | 5 ++- nativelink-util/src/fs.rs | 2 +- 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/nativelink-redis-tester/src/dynamic_fake_redis.rs b/nativelink-redis-tester/src/dynamic_fake_redis.rs index ec33d3494..d3c408d00 100644 --- a/nativelink-redis-tester/src/dynamic_fake_redis.rs +++ b/nativelink-redis-tester/src/dynamic_fake_redis.rs @@ -22,6 +22,7 @@ use redis::Value; use redis_protocol::resp2::decode::decode; use redis_protocol::resp2::types::{OwnedFrame, Resp2Frame}; use tokio::net::TcpListener; +use tokio::sync::oneshot::{self, Sender}; use tracing::{debug, info, trace}; use crate::fake_redis::{arg_as_string, fake_redis_internal}; @@ -70,7 +71,7 @@ impl FakeRedisBackend { .replace(subscription_manager); } - async fn dynamic_fake_redis(self, listener: TcpListener) { + async fn dynamic_fake_redis(self, listener: TcpListener, listener_ready_tx: Sender<()>) { let inner = move |buf: &[u8]| -> String { let mut output = String::new(); let mut buf_index = 0; @@ -399,7 +400,7 @@ impl FakeRedisBackend { } output }; - fake_redis_internal(listener, vec![inner]).await; + fake_redis_internal(listener, listener_ready_tx, vec![inner]).await; } pub async fn run(self) -> u16 { @@ -407,10 +408,16 @@ impl FakeRedisBackend { let port = listener.local_addr().unwrap().port(); info!("Using port {port}"); + let (listener_ready_tx, listener_ready_rx) = oneshot::channel::<()>(); + background_spawn!("listener", async move { - self.dynamic_fake_redis(listener).await; + self.dynamic_fake_redis(listener, listener_ready_tx).await; }); + listener_ready_rx + .await + .expect("Expected successful listener boot"); + port } } diff --git a/nativelink-redis-tester/src/fake_redis.rs b/nativelink-redis-tester/src/fake_redis.rs index d80ed2152..95a49685b 100644 --- a/nativelink-redis-tester/src/fake_redis.rs +++ b/nativelink-redis-tester/src/fake_redis.rs @@ -21,6 +21,7 @@ use redis::Value; use redis_test::IntoRedisValue; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; +use tokio::sync::oneshot::{self, Sender}; use tracing::{error, info, warn}; fn cmd_as_string(cmd: &redis::Cmd) -> String { @@ -198,16 +199,22 @@ pub fn fake_redis_sentinel_stream(master_name: &str, redis_port: u16) -> HashMap response } -pub(crate) async fn fake_redis_internal(listener: TcpListener, handlers: Vec) -where +pub(crate) async fn fake_redis_internal( + listener: TcpListener, + listener_ready_tx: Sender<()>, + handlers: Vec, +) where H: Fn(&[u8]) -> String + Send + Clone + 'static + Sync, { let mut handler_iter = handlers.iter().cloned().cycle(); + info!( + "Waiting for connection on {}", + listener.local_addr().unwrap() + ); + listener_ready_tx + .send(()) + .expect("Expected successful send"); loop { - info!( - "Waiting for connection on {}", - listener.local_addr().unwrap() - ); let Ok((mut stream, _)) = listener.accept().await else { error!("accept error"); panic!("error"); @@ -229,8 +236,11 @@ where } } -async fn fake_redis(listener: TcpListener, all_responses: Vec>) -where +async fn fake_redis( + listener: TcpListener, + listener_ready_tx: Sender<()>, + all_responses: Vec>, +) where B: BuildHasher + Clone + Send + 'static + Sync, { let funcs = all_responses @@ -254,7 +264,7 @@ where } }) .collect(); - fake_redis_internal(listener, funcs).await; + fake_redis_internal(listener, listener_ready_tx, funcs).await; } async fn make_fake_redis_with_multiple_responses( @@ -262,12 +272,18 @@ async fn make_fake_redis_with_multiple_responses u16 { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let port = listener.local_addr().unwrap().port(); - info!(port, "Fake redis booted"); + + let (listener_ready_tx, listener_ready_rx) = oneshot::channel::<()>(); background_spawn!("listener", async move { - fake_redis(listener, responses).await; + fake_redis(listener, listener_ready_tx, responses).await; }); + listener_ready_rx + .await + .expect("Expected successful listener boot"); + info!(port, "Fake redis booted"); + port } diff --git a/nativelink-redis-tester/src/read_only_redis.rs b/nativelink-redis-tester/src/read_only_redis.rs index fe5e92a9c..6e6dac730 100644 --- a/nativelink-redis-tester/src/read_only_redis.rs +++ b/nativelink-redis-tester/src/read_only_redis.rs @@ -22,6 +22,7 @@ use redis::Value; use redis_protocol::resp2::decode::decode; use redis_protocol::resp2::types::OwnedFrame; use tokio::net::TcpListener; +use tokio::sync::oneshot::{self, Sender}; use tracing::info; use crate::fake_redis::{arg_as_string, fake_redis_internal}; @@ -47,7 +48,7 @@ impl ReadOnlyRedis { } } - async fn dynamic_fake_redis(self, listener: TcpListener) { + async fn dynamic_fake_redis(self, listener: TcpListener, listener_ready_tx: Sender<()>) { let readonly_err_str = "READONLY You can't write against a read only replica."; let readonly_err = format!("!{}\r\n{readonly_err_str}\r\n", readonly_err_str.len()); @@ -149,7 +150,7 @@ impl ReadOnlyRedis { } output }; - fake_redis_internal(listener, vec![inner]).await; + fake_redis_internal(listener, listener_ready_tx, vec![inner]).await; } pub async fn run(self) -> u16 { @@ -157,10 +158,16 @@ impl ReadOnlyRedis { let port = listener.local_addr().unwrap().port(); info!("Using port {port}"); + let (listener_ready_tx, listener_ready_rx) = oneshot::channel::<()>(); + background_spawn!("listener", async move { - self.dynamic_fake_redis(listener).await; + self.dynamic_fake_redis(listener, listener_ready_tx).await; }); + listener_ready_rx + .await + .expect("Expected successful listener boot"); + port } } diff --git a/nativelink-store/src/filesystem_store.rs b/nativelink-store/src/filesystem_store.rs index 3624e17c7..521159044 100644 --- a/nativelink-store/src/filesystem_store.rs +++ b/nativelink-store/src/filesystem_store.rs @@ -1224,6 +1224,7 @@ impl FilesystemStore { .await; return Err(err); } + trace!(?key, "Finished emplace file"); encoded_file_path.path_type = PathType::Content; encoded_file_path.key = key; Ok(()) diff --git a/nativelink-store/tests/filesystem_store_test.rs b/nativelink-store/tests/filesystem_store_test.rs index 0e59d883e..3df09b9da 100644 --- a/nativelink-store/tests/filesystem_store_test.rs +++ b/nativelink-store/tests/filesystem_store_test.rs @@ -51,7 +51,7 @@ use tokio::sync::{Barrier, Semaphore}; use tokio::time::sleep; use tokio_stream::StreamExt; use tokio_stream::wrappers::ReadDirStream; -use tracing::{Instrument, debug}; +use tracing::{Instrument, debug, info}; const VALID_HASH: &str = "0123456789abcdef000000000000000000010000000000000123456789abcdef"; @@ -1765,7 +1765,8 @@ async fn detect_duplicate_upload() -> Result<(), Error> { let key = &StoreKey::Digest(digest); let temp_key = make_temp_key(key); - let (mut entry, mut temp_file, _temp_full_path) = store.make_temp_file(temp_key).await?; + let (mut entry, mut temp_file, temp_full_path) = store.make_temp_file(temp_key).await?; + info!(?temp_full_path, "Temp full path"); let mut data = Bytes::from_static(VALUE1.as_bytes()); temp_file.write_all_buf(&mut data).await?; *entry.data_size_mut() = 10; diff --git a/nativelink-util/src/fs.rs b/nativelink-util/src/fs.rs index bf5d95e40..47c5ccf3f 100644 --- a/nativelink-util/src/fs.rs +++ b/nativelink-util/src/fs.rs @@ -440,7 +440,7 @@ fn internal_remove_dir_all(path: impl AsRef) -> Result<(), Error> { for entry in WalkDir::new(&path) { let Ok(entry) = &entry else { - debug!("Can't get into {entry:?}, assuming already deleted"); + debug!(?entry, "Can't get entry, assuming already deleted"); continue; }; let metadata = entry.metadata()?; From 3bfe40897a02853635ba00107d6c193885799eea Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Fri, 26 Jun 2026 00:59:34 +0530 Subject: [PATCH 008/144] web: Update website components (#2486) Correct inaccurate stats and claims, restore the real figures, and remove outdated demo components: - home: 10B+ -> 1B+ build requests; remove the AI-assisted terminal demo and its orphaned components - product: "10x average speedup" -> "4-15x faster builds" - resources: remove the placeholder case studies, the funding post, and the placeholder blog cards; keep the LLVM write-up and the LRE talk - company: remove the story timeline - pricing: remove the trust strip and the SLA row; price unchanged - status: remove the page and its footer/sitemap links --- web/apps/web/app/company/page.tsx | 57 ---- web/apps/web/app/page.tsx | 23 +- web/apps/web/app/pricing/page.tsx | 25 -- web/apps/web/app/product/page.tsx | 4 +- web/apps/web/app/resources/page.tsx | 157 +---------- web/apps/web/app/sitemap.ts | 1 - web/apps/web/app/status/page.tsx | 207 -------------- web/apps/web/components/animated-terminal.tsx | 263 ------------------ web/apps/web/components/terminal-data.ts | 259 ----------------- web/apps/web/components/terminal-demo.tsx | 52 ---- .../ui/src/components/site-footer.tsx | 11 - 11 files changed, 4 insertions(+), 1055 deletions(-) delete mode 100644 web/apps/web/app/status/page.tsx delete mode 100644 web/apps/web/components/animated-terminal.tsx delete mode 100644 web/apps/web/components/terminal-data.ts delete mode 100644 web/apps/web/components/terminal-demo.tsx diff --git a/web/apps/web/app/company/page.tsx b/web/apps/web/app/company/page.tsx index fe479c5dd..51598a596 100644 --- a/web/apps/web/app/company/page.tsx +++ b/web/apps/web/app/company/page.tsx @@ -31,29 +31,6 @@ const values = [ }, ]; -const milestones = [ - { - year: "2023", - title: "Trace Machina founded.", - body: "Aaron Mondal & team start building NativeLink in Rust, in public, on GitHub.", - }, - { - year: "2024", - title: "First billion requests served.", - body: "Open-source NativeLink hits a billion build requests / month in production.", - }, - { - year: "2024", - title: "NativeLink Cloud launches.", - body: "Managed offering goes GA after a private beta with five infrastructure-scale teams.", - }, - { - year: "2025", - title: "LLVM picks up NativeLink.", - body: "LLVM contributors adopt CMake + recc + NativeLink — clang builds 4× faster on day one.", - }, -]; - const contactCards = [ { title: "Media kit", @@ -150,40 +127,6 @@ export default function CompanyPage() { - {/* TIMELINE */} -
- -
- Story -

- How we got here. -

-
-
-
-
    - {milestones.map((m, i) => ( - -
  1. -
    - -
    -
    - - {m.year} - -
    -

    - {m.title} -

    -

    {m.body}

    -
  2. -
    - ))} -
-
-
- {/* CONTACT */}
diff --git a/web/apps/web/app/page.tsx b/web/apps/web/app/page.tsx index 1b70b1fb1..eb6763935 100644 --- a/web/apps/web/app/page.tsx +++ b/web/apps/web/app/page.tsx @@ -1,4 +1,3 @@ -import { AnimatedTerminal } from "@/components/animated-terminal"; import { ArchitectureDiagram } from "@/components/architecture-diagram"; import { CitrixLogo, @@ -75,7 +74,7 @@ const stats = [ body: "Demonstrated on LLVM, one of the world's largest C++ codebases.", }, { - value: "10B+", + value: "1B+", label: "build requests per month", body: "served in production.", }, @@ -226,26 +225,6 @@ export default function HomePage() {
-
-
-
- - - - - Quick start -

- Built for AI-assisted development. -

-

- Start in 10 minutes with Docker. Develop with Claude Code, Cursor or Codex skills that - guide storage changes, config updates, debugging, and multi-worker test clusters, the - same workflows NativeLink engineers use in production. -

-
-
-
-
diff --git a/web/apps/web/app/pricing/page.tsx b/web/apps/web/app/pricing/page.tsx index 94d8500d6..78e395001 100644 --- a/web/apps/web/app/pricing/page.tsx +++ b/web/apps/web/app/pricing/page.tsx @@ -102,7 +102,6 @@ const comparison: { cloud: "Email + Slack", ent: "Dedicated engineer", }, - { label: "SLA", oss: false, cloud: "99.9%", ent: "Custom" }, { label: "Onboarding", oss: false, cloud: true, ent: "White-glove" }, ], }, @@ -223,30 +222,6 @@ export default function PricingPage() {
- {/* TRUST STRIP */} -
- -
-
-
99.99%
-
Cloud uptime, 12-mo trailing
-
-
-
<1ms
-
p99 cache lookup
-
-
-
SOC 2
-
In progress — Q3
-
-
-
30 days
-
No-questions trial on Cloud
-
-
-
-
- {/* COMPARISON */}
diff --git a/web/apps/web/app/product/page.tsx b/web/apps/web/app/product/page.tsx index f28a3b08c..b8e5b947b 100644 --- a/web/apps/web/app/product/page.tsx +++ b/web/apps/web/app/product/page.tsx @@ -26,8 +26,8 @@ const pillars = [ eyebrow: "Remote build execution", title: "Distribute across every core you have.", body: "Offload compilation and tests to a worker fleet that scales horizontally — on AWS, GCP, or bare metal. Hermetic by design, deterministic by default. Specialized hardware (GPUs, ARM, Apple Silicon) supported natively.", - metric: "10×", - metricLabel: "average speedup", + metric: "4-15×", + metricLabel: "faster builds", icon: ( <> ), diff --git a/web/apps/web/app/resources/page.tsx b/web/apps/web/app/resources/page.tsx index 89d273e8b..80e404bf7 100644 --- a/web/apps/web/app/resources/page.tsx +++ b/web/apps/web/app/resources/page.tsx @@ -1,6 +1,5 @@ import { Badge, - Button, Eyebrow, Reveal, Section, @@ -23,77 +22,13 @@ const featuredPost = { }; const announcements = [ - { - tag: "Announcement", - title: "NativeLink v1.3 — Persistent workers, faster scheduling, GPU support.", - excerpt: "The biggest release this quarter. Workers stay warm between actions, the scheduler dispatches 3× faster, and we now ship first-class CUDA/ROCm support.", - date: "May 02, 2026", - readingTime: "5 min", - accent: "brand" as const, - }, { tag: "Talk", title: "Hermetic toolchain creation with LRE & Nix", excerpt: "Aaron Mondal walks through Local Remote Execution — running fully hermetic Bazel builds on your own laptop, no Docker required.", date: "April 18, 2026", readingTime: "32 min video", - accent: "default" as const, - }, - { - tag: "Announcement", - title: "Trace Machina raises $15M to build the Bazel-grade cache for everyone.", - excerpt: "We're hiring across systems, distributed storage, and developer experience.", - date: "March 28, 2026", - readingTime: "3 min", - accent: "default" as const, - }, -]; - -const caseStudies = [ - { - company: "Samsung Internet", - tagline: "Browser builds, 6× faster.", - summary: "Samsung's Chromium-based browser team adopted NativeLink to cut their per-commit CI wall-time from 38 minutes to under 7.", - metric: "6×", - }, - { - company: "Aurora Robotics", - tagline: "Simulation that keeps up with the road.", - summary: "Replay-driven sim that used to chew through nightly fleets now finishes during code review — and the cache survives every rebase.", - metric: "94%", - }, - { - company: "Cirque Semi", - tagline: "Verification across 4,000 cores.", - summary: "An EDA shop with a notoriously hot Verilog regression suite now hands every job to NativeLink's scheduler. Tape-out velocity 2.3× higher.", - metric: "2.3×", - }, -]; - -const blogPosts = [ - { - tag: "Engineering", - title: "Why we rewrote our scheduler in async Rust.", - excerpt: "How we shaved p99 latency from 24ms to 1.8ms by ditching threads, embracing tokio, and being honest about lock contention.", - date: "Apr 21", - }, - { - tag: "Deep dive", - title: "Content-addressed storage at a billion requests a month.", - excerpt: "Sharding strategy, hot-key handling, and the surprising thing that happens when half your fleet asks for the same blob at once.", - date: "Apr 04", - }, - { - tag: "Tutorial", - title: "Migrate a Bazel monorepo to remote execution in an afternoon.", - excerpt: "A step-by-step playbook with real configs, common pitfalls, and the diff between a 12-minute build and an 80-second one.", - date: "Mar 19", - }, - { - tag: "Engineering", - title: "Hermetic builds without Docker — how LRE works.", - excerpt: "Nix profiles + content-addressed inputs let you reproduce CI exactly on your laptop. No containers, no Dockerfiles, no surprises.", - date: "Mar 05", + accent: "default", }, ]; @@ -253,96 +188,6 @@ export default function ResourcesPage() {
- {/* CASE STUDIES */} -
- -
-
- Case studies -

- In production. -

-
-

- How teams are using NativeLink to keep their build farms — and their - engineers — moving at full speed. -

-
-
- -
- {caseStudies.map((cs, i) => ( - -
-
- - {cs.company} - -
- {cs.metric} -
-
-

- {cs.tagline} -

-

- {cs.summary} -

-
- Read case study -
-
-
- ))} -
-
- - {/* BLOG */} -
- -
-
- Blog -

- Recent writing -

-
- -
-
- - -
); } diff --git a/web/apps/web/app/sitemap.ts b/web/apps/web/app/sitemap.ts index 4796a488b..79203d845 100644 --- a/web/apps/web/app/sitemap.ts +++ b/web/apps/web/app/sitemap.ts @@ -11,7 +11,6 @@ const routes: { path: string; changeFrequency: MetadataRoute.Sitemap[number]["ch { path: "/resources", changeFrequency: "weekly", priority: 0.8 }, { path: "/contact", changeFrequency: "monthly", priority: 0.7 }, { path: "/license", changeFrequency: "yearly", priority: 0.4 }, - { path: "/status", changeFrequency: "daily", priority: 0.5 }, { path: "/terms", changeFrequency: "yearly", priority: 0.3 }, { path: "/compliance", changeFrequency: "yearly", priority: 0.4 }, ]; diff --git a/web/apps/web/app/status/page.tsx b/web/apps/web/app/status/page.tsx deleted file mode 100644 index d592657f1..000000000 --- a/web/apps/web/app/status/page.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import { Eyebrow, Reveal, Section, cn } from "@nativelink/ui"; - -export const metadata = { - title: "Status", - description: "Real-time status of NativeLink Cloud services.", -}; - -type Health = "operational" | "degraded" | "outage" | "maintenance"; - -const overall: Health = "operational"; - -const components: { name: string; description: string; status: Health; uptime: string }[] = [ - { name: "Cache (CAS)", description: "Content-addressed storage", status: "operational", uptime: "99.998%" }, - { name: "Action Cache", description: "Action result lookups", status: "operational", uptime: "99.997%" }, - { name: "Scheduler", description: "Build dispatch & coordination", status: "operational", uptime: "99.995%" }, - { name: "Workers (us-east)", description: "Execution fleet — US East", status: "operational", uptime: "99.99%" }, - { name: "Workers (eu-west)", description: "Execution fleet — Europe", status: "operational", uptime: "99.99%" }, - { name: "Dashboard", description: "Web console & APIs", status: "operational", uptime: "99.99%" }, - { name: "Webhooks", description: "Outbound event delivery", status: "operational", uptime: "99.97%" }, -]; - -const incidents = [ - { - date: "May 14, 2026", - duration: "23 minutes", - severity: "Minor", - title: "Increased latency on us-east scheduler", - body: "A noisy neighbor in our control plane briefly inflated p99 scheduler latency to 800ms. Mitigated by failing over to the standby instance. No build failures.", - resolved: true, - }, - { - date: "Apr 28, 2026", - duration: "8 minutes", - severity: "Minor", - title: "Brief webhook delivery delay", - body: "Outbound webhook deliveries queued for ~8 minutes during a routine TLS-cert rotation. Cleared once the new cert propagated.", - resolved: true, - }, - { - date: "Mar 09, 2026", - duration: "1h 12m", - severity: "Major", - title: "Cache write degradation — eu-west", - body: "A single CAS shard saturated during an unusually large rollout. We re-sharded the hot key, added rate limiting, and pushed the post-mortem here in resources/blog.", - resolved: true, - }, -]; - -const statusTone: Record = { - operational: { dot: "bg-success", label: "Operational", text: "text-success" }, - degraded: { dot: "bg-amber-500", label: "Degraded performance", text: "text-amber-500" }, - outage: { dot: "bg-red-500", label: "Outage", text: "text-red-500" }, - maintenance: { dot: "bg-brand", label: "Maintenance", text: "text-brand" }, -}; - -// Per-component random-looking uptime bar (90 days), with all green for now. -function UptimeBar() { - return ( -
- {Array.from({ length: 60 }).map((_, i) => ( - - ))} -
- ); -} - -export default function StatusPage() { - const ov = statusTone[overall]; - return ( - <> - {/* HERO */} -
-
-
- -
- Status -
- - - - - - All systems operational - -
-

- Live status of NativeLink Cloud. -

-

- Component-level health, 90-day uptime, and the last few incidents. - Subscribe to incident updates by email at{" "} - - status@nativelink.com - - . -

-
-
-
-
- - {/* COMPONENTS */} -
- -
-
-

- Components -

-

Last 60 days

-
-
    - {components.map((c) => { - const tone = statusTone[c.status]; - return ( -
  • -
    -

    {c.name}

    -

    {c.description}

    -
    - -
    - {c.uptime} - - - - {tone.label} - - -
    -
  • - ); - })} -
-
-
-
- - {/* INCIDENTS */} -
- -
-
- Past 90 days -

- Recent incidents -

-
-
-
- -
    - {incidents.map((inc, i) => ( - -
  1. -
    - - {inc.severity} - - - {inc.date} · {inc.duration} - - {inc.resolved ? ( - - - Resolved - - ) : null} -
    -

    - {inc.title} -

    -

    - {inc.body} -

    -
  2. -
    - ))} -
-
- - ); -} diff --git a/web/apps/web/components/animated-terminal.tsx b/web/apps/web/components/animated-terminal.tsx deleted file mode 100644 index 16ea3e4da..000000000 --- a/web/apps/web/components/animated-terminal.tsx +++ /dev/null @@ -1,263 +0,0 @@ -"use client"; - -import { useEffect, useRef, useState } from "react"; -import type { TerminalLine } from "./terminal-data"; -import { terminalTabs } from "./terminal-data"; - -interface DisplayedLine { - id: number; - text: string; -} - -const cx = (...classes: Array) => - classes.filter(Boolean).join(" "); - -const lineColorClass = (line: string) => { - if (line.startsWith("$")) { - return "text-purple-400 font-semibold"; - } - if (line.startsWith("Using /")) { - return "text-blue-400"; - } - if (line.startsWith(" ")) { - return "text-gray-400"; - } - if ( - line.includes("✓") || - line.includes("passed") || - line.includes("complete") || - line.includes("ready") - ) { - return "text-green-400"; - } - if (line.includes("Generating") || line.includes("tokens") || line.includes("thought for")) { - return "text-cyan-300"; - } - if (line.includes("INFO")) { - return "text-cyan-400"; - } - return "text-gray-200"; -}; - -const isShellCommand = (line: TerminalLine) => - line.text.startsWith("curl") || line.text.startsWith("docker"); - -const isCommand = (line: TerminalLine) => isShellCommand(line) || line.text.startsWith(">"); - -export function AnimatedTerminal() { - const [activeTab, setActiveTab] = useState(0); - const [displayedLines, setDisplayedLines] = useState([]); - const [currentInput, setCurrentInput] = useState(""); - const [isTyping, setIsTyping] = useState(false); - const terminalRef = useRef(null); - - useEffect(() => { - setDisplayedLines([]); - setCurrentInput(""); - setIsTyping(false); - - const currentTab = terminalTabs[activeTab]; - if (!currentTab) { - return; - } - - const timeoutIds: number[] = []; - let isCancelled = false; - let lineIndex = 0; - let nextLineId = 0; - let linesSnapshot: DisplayedLine[] = []; - - const schedule = (callback: () => void, delay: number) => { - const timeoutId = window.setTimeout(() => { - if (!isCancelled) { - callback(); - } - }, delay); - timeoutIds.push(timeoutId); - }; - - const scrollToBottom = () => { - if (isCancelled) { - return; - } - if (terminalRef.current) { - terminalRef.current.scrollTop = terminalRef.current.scrollHeight; - } - }; - - const appendLine = (text: string) => { - if (isCancelled) { - return; - } - linesSnapshot = [...linesSnapshot, { id: nextLineId++, text }]; - setDisplayedLines(linesSnapshot); - }; - - const replaceLastLine = (text: string) => { - if (isCancelled) { - return; - } - const nextLines = [...linesSnapshot]; - const lastIndex = nextLines.length - 1; - - if (lastIndex >= 0) { - const lastLine = nextLines[lastIndex]; - if (lastLine) { - nextLines[lastIndex] = { ...lastLine, text }; - linesSnapshot = nextLines; - setDisplayedLines(linesSnapshot); - } - } - }; - - const lastLineText = () => linesSnapshot[linesSnapshot.length - 1]?.text; - - const resetLines = () => { - if (isCancelled) { - return; - } - linesSnapshot = []; - setDisplayedLines([]); - setCurrentInput(""); - setIsTyping(false); - }; - - const showStatus = (line: TerminalLine) => { - const statusText = line.text.substring(7); - - if (lastLineText()?.startsWith("Generating..")) { - replaceLastLine(statusText); - } else { - appendLine(statusText); - } - }; - - const showDownload = (line: TerminalLine) => { - const [layerId = ""] = line.text.split(":"); - const lastLine = lastLineText(); - - if (lastLine?.includes(layerId) && lastLine.includes("Downloading")) { - replaceLastLine(line.text); - } else { - appendLine(line.text); - } - }; - - const animate = () => { - if (isCancelled) { - return; - } - if (lineIndex >= currentTab.lines.length) { - schedule(() => { - resetLines(); - lineIndex = 0; - animate(); - }, 3000); - return; - } - - const line = currentTab.lines[lineIndex]; - if (!line) { - return; - } - - if (isCommand(line)) { - if (line.instant) { - const prefix = isShellCommand(line) ? "$ " : ""; - appendLine(prefix + line.text); - lineIndex++; - scrollToBottom(); - schedule(animate, line.delay ?? 300); - return; - } - - setIsTyping(true); - setCurrentInput(line.text); - - schedule(() => { - if (isCancelled) { - return; - } - const prefix = isShellCommand(line) ? "$ " : ""; - appendLine(prefix + line.text); - setCurrentInput(""); - setIsTyping(false); - lineIndex++; - scrollToBottom(); - schedule(animate, line.delay ?? 300); - }, 800); - return; - } - - if (line.text.startsWith("STATUS:")) { - showStatus(line); - } else if (line.text.includes("Downloading")) { - showDownload(line); - } else { - appendLine(line.text); - } - - lineIndex++; - scrollToBottom(); - schedule(animate, line.instant ? (line.delay ?? 0) : (line.delay ?? 100)); - }; - - animate(); - - return () => { - isCancelled = true; - for (const timeoutId of timeoutIds) { - window.clearTimeout(timeoutId); - } - }; - }, [activeTab]); - - return ( -
-
- {terminalTabs.map((tab, index) => ( - - ))} -
- -
-
-
- {displayedLines.map((line) => ( -
- {line.text} -
- ))} -
-
- -
-
- {">"} -
- {currentInput} - {isTyping && ( - - )} -
-
-
-
-
- ); -} diff --git a/web/apps/web/components/terminal-data.ts b/web/apps/web/components/terminal-data.ts deleted file mode 100644 index 640b25b51..000000000 --- a/web/apps/web/components/terminal-data.ts +++ /dev/null @@ -1,259 +0,0 @@ -export interface TerminalLine { - text: string; - delay?: number; - instant?: boolean; -} - -export interface TerminalTab { - name: string; - lines: TerminalLine[]; -} - -export const LATEST_NATIVELINK_RELEASE_TAG = "v1.5.2"; - -export const terminalTabs: TerminalTab[] = [ - { - name: "Quick Start", - lines: [ - { - text: `curl -O https://raw.githubusercontent.com/TraceMachina/nativelink/${LATEST_NATIVELINK_RELEASE_TAG}/nativelink-config/examples/basic_cas.json5`, - delay: 0, - instant: true, - }, - { text: " % Total % Received Time", delay: 200, instant: true }, - { text: "100 2841 100 2841 0:00:01", delay: 50, instant: true }, - { text: "", delay: 300 }, - { - text: `docker run -v $(pwd)/basic_cas.json5:/config -p 50051:50051 ghcr.io/tracemachina/nativelink:${LATEST_NATIVELINK_RELEASE_TAG} config`, - delay: 200, - instant: true, - }, - { text: "", delay: 400 }, - { text: "Unable to find image locally", delay: 200, instant: true }, - { - text: `${LATEST_NATIVELINK_RELEASE_TAG}: Pulling from tracemachina/nativelink`, - delay: 100, - instant: true, - }, - { text: "a1d0c7532777: Downloading [=> ] 15%", delay: 120, instant: true }, - { text: "a1d0c7532777: Downloading [=======> ] 45%", delay: 120, instant: true }, - { text: "a1d0c7532777: Downloading [==============> ] 75%", delay: 120, instant: true }, - { text: "a1d0c7532777: Downloading [================] 100%", delay: 120, instant: true }, - { text: "a1d0c7532777: Pull complete", delay: 200, instant: true }, - { text: "7f9a694b6f8c: Downloading [====> ] 25%", delay: 120, instant: true }, - { text: "7f9a694b6f8c: Downloading [=========> ] 60%", delay: 120, instant: true }, - { text: "7f9a694b6f8c: Downloading [==============> ] 90%", delay: 120, instant: true }, - { text: "7f9a694b6f8c: Downloading [================] 100%", delay: 120, instant: true }, - { text: "7f9a694b6f8c: Pull complete", delay: 200, instant: true }, - { text: "Status: Downloaded newer image", delay: 200, instant: true }, - { text: "", delay: 400 }, - { text: "INFO nativelink::config: Loading config from /config", delay: 200, instant: true }, - { - text: "INFO nativelink::cas_server: CAS server listening on 0.0.0.0:50051", - delay: 150, - instant: true, - }, - { text: "INFO nativelink::scheduler: Scheduler initialized", delay: 150, instant: true }, - { text: "", delay: 300 }, - { text: "✓ Ready to accept builds", delay: 300, instant: true }, - ], - }, - { - name: "AI Dev", - lines: [ - { text: "> Add retry logic to storage layer", delay: 0 }, - { text: "", delay: 400 }, - { - text: "STATUS:Generating.. (2s · ↑ 3.2k tokens · thought for 1s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (4s · ↑ 3.3k tokens · thought for 2s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (6s · ↑ 3.4k tokens · thought for 3s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (8s · ↑ 3.4k tokens · thought for 3s)", - delay: 150, - instant: true, - }, - { text: "", delay: 300 }, - { text: "Using /nativelink-rust-change skill...", delay: 300, instant: true }, - { text: "Worktree: feature/storage-retry", delay: 250, instant: true }, - { text: "", delay: 300 }, - { text: "Inspecting StorageBackend trait and implementations...", delay: 350, instant: true }, - { text: "Adding exponential backoff to fast_slow_store.rs...", delay: 350, instant: true }, - { text: " - Max retries: 3", delay: 200, instant: true }, - { text: " - Backoff: 100ms → 400ms → 1600ms", delay: 200, instant: true }, - { text: "", delay: 400 }, - { text: "Running tests: bazel test //nativelink-store/...", delay: 300, instant: true }, - { text: "", delay: 500 }, - { text: "✓ All 32 tests passed", delay: 300, instant: true }, - { text: "", delay: 200 }, - { text: "(Total: 18s · ↑ 3.4k tokens · ↓ 1.2k tokens)", delay: 300, instant: true }, - { text: "", delay: 200 }, - { text: "Modified: nativelink-store/src/fast_slow_store.rs", delay: 250, instant: true }, - { text: "", delay: 600 }, - { text: "> push this to github", delay: 0, instant: true }, - { text: "", delay: 400 }, - { text: "git push origin feature/storage-retry", delay: 250, instant: true }, - { text: "Enumerating objects: 7, done.", delay: 200, instant: true }, - { text: "Counting objects: 100% (7/7), done.", delay: 150, instant: true }, - { - text: "Writing objects: 100% (4/4), 1.2 KiB | 1.2 MiB/s, done.", - delay: 150, - instant: true, - }, - { text: "remote: Resolving deltas: 100% (2/2), done.", delay: 200, instant: true }, - { text: "To github.com:TraceMachina/nativelink.git", delay: 150, instant: true }, - { - text: " 3f7a8e9..b2c4d6f feature/storage-retry -> feature/storage-retry", - delay: 200, - instant: true, - }, - { text: "", delay: 600 }, - { text: "remote: GitHub Actions: Workflow started", delay: 300, instant: true }, - { text: "remote: Running: bazel test //... (remote cache)", delay: 300, instant: true }, - { text: "remote: ✓ All checks passed in 1.2s", delay: 400, instant: true }, - ], - }, - { - name: "Config Update", - lines: [ - { text: "> Enable S3 caching with 7-day TTL", delay: 0 }, - { text: "", delay: 400 }, - { - text: "STATUS:Generating.. (2s · ↑ 2.7k tokens · thought for 1s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (4s · ↑ 2.8k tokens · thought for 2s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (6s · ↑ 2.9k tokens · thought for 2s)", - delay: 150, - instant: true, - }, - { text: "", delay: 300 }, - { text: "Using /nativelink-config-protocol skill...", delay: 300, instant: true }, - { text: "Updating config schema and examples...", delay: 350, instant: true }, - { text: " + S3Store backend option", delay: 250, instant: true }, - { text: " + Default TTL: 604800s (7 days)", delay: 250, instant: true }, - { text: " ✓ Backward compatible", delay: 300, instant: true }, - { text: "", delay: 400 }, - { text: "Running validation: bazel test //nativelink-config/...", delay: 300, instant: true }, - { text: "", delay: 500 }, - { text: "✓ All 18 tests passed", delay: 300, instant: true }, - { text: "✓ Example configs load successfully", delay: 250, instant: true }, - { text: "", delay: 200 }, - { text: "(Total: 14s · ↑ 2.9k tokens · ↓ 980 tokens)", delay: 300, instant: true }, - { text: "", delay: 200 }, - { text: "Modified: nativelink-config/examples/s3_cache.json5", delay: 250, instant: true }, - ], - }, - { - name: "Debug Worker", - lines: [ - { text: "> Debug LLVM build timeout on worker-us-east-1a-003", delay: 0 }, - { text: "", delay: 400 }, - { - text: "STATUS:Generating.. (3s · ↑ 3.9k tokens · thought for 2s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (6s · ↑ 4.0k tokens · thought for 3s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (9s · ↑ 4.1k tokens · thought for 4s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (11s · ↑ 4.1k tokens · thought for 5s)", - delay: 150, - instant: true, - }, - { text: "", delay: 300 }, - { text: "Using /nativelink-lre-debug skill...", delay: 300, instant: true }, - { text: "Checking server status and collecting logs...", delay: 350, instant: true }, - { text: " Server: 127.0.0.1:50051 ✓", delay: 250, instant: true }, - { text: " Worker: worker-us-east-1a-003", delay: 250, instant: true }, - { text: " Action: compile_llvm_target (timeout after 900s)", delay: 300, instant: true }, - { text: "", delay: 400 }, - { text: "Classifying failure across subsystems...", delay: 350, instant: true }, - { text: " Storage (CAS/AC): ✓", delay: 250, instant: true }, - { text: " Scheduling: ✓", delay: 250, instant: true }, - { text: " Execution: Issue detected", delay: 300, instant: true }, - { text: "", delay: 400 }, - { text: "Root cause:", delay: 250, instant: true }, - { text: " Worker timeout (900s) < compile time (1200s)", delay: 350, instant: true }, - { text: "", delay: 400 }, - { text: "Recommendation:", delay: 250, instant: true }, - { text: " Increase timeout to 1800s in worker.json5", delay: 300, instant: true }, - { text: "", delay: 200 }, - { text: "(Total: 22s · ↑ 4.1k tokens · ↓ 1.5k tokens)", delay: 300, instant: true }, - ], - }, - { - name: "Parallel Builds", - lines: [ - { text: "> start a local nativelink cluster with 4 workers", delay: 0 }, - { text: "", delay: 400 }, - { - text: "STATUS:Generating.. (1s · ↑ 1.2k tokens · thought for 0s)", - delay: 150, - instant: true, - }, - { - text: "STATUS:Generating.. (2s · ↑ 1.3k tokens · thought for 1s)", - delay: 150, - instant: true, - }, - { text: "", delay: 300 }, - { text: "Using /nativelink-deploy skill...", delay: 300, instant: true }, - { text: "", delay: 250 }, - { text: "Setting up Docker Compose with 4 worker instances...", delay: 300, instant: true }, - { text: "", delay: 300 }, - { text: "Creating network nativelink_default", delay: 200, instant: true }, - { text: "Creating volume cas-data", delay: 150, instant: true }, - { text: "", delay: 300 }, - { text: "Starting scheduler... done", delay: 250, instant: true }, - { text: "Starting worker-1... done", delay: 200, instant: true }, - { text: "Starting worker-2... done", delay: 200, instant: true }, - { text: "Starting worker-3... done", delay: 200, instant: true }, - { text: "Starting worker-4... done", delay: 200, instant: true }, - { text: "", delay: 400 }, - { text: "scheduler | INFO: 4 workers registered", delay: 300, instant: true }, - { text: "", delay: 500 }, - { - text: "bazel build //... --remote_executor=grpc://127.0.0.1:50052 --jobs=20", - delay: 300, - instant: true, - }, - { text: "", delay: 400 }, - { text: "INFO: Analyzed 847 targets", delay: 250, instant: true }, - { text: "INFO: Found 847 targets...", delay: 200, instant: true }, - { text: "", delay: 600 }, - { text: "[0 / 847] checking cached actions", delay: 300, instant: true }, - { text: "[184 / 847] 4 actions running", delay: 400, instant: true }, - { text: "[512 / 847] 4 actions running", delay: 400, instant: true }, - { text: "[823 / 847] 4 actions running", delay: 400, instant: true }, - { text: "", delay: 300 }, - { text: "INFO: Elapsed time: 47.3s, Critical Path: 12.8s", delay: 300, instant: true }, - { text: "INFO: 847 processes: 823 remote, 24 internal", delay: 250, instant: true }, - { text: "✓ Build completed successfully", delay: 300, instant: true }, - ], - }, -]; diff --git a/web/apps/web/components/terminal-demo.tsx b/web/apps/web/components/terminal-demo.tsx deleted file mode 100644 index 4df158e4b..000000000 --- a/web/apps/web/components/terminal-demo.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { cn } from "@nativelink/ui"; - -interface Line { - prompt?: boolean; - comment?: boolean; - info?: boolean; - children: string; -} - -interface TerminalDemoProps { - label?: string; - lines: Line[]; - className?: string; -} - -export function TerminalDemo({ - label = "shell — quickstart", - lines, - className, -}: TerminalDemoProps) { - return ( -
-
- - - - {label} -
-
-        
-          {lines.map((line, i) => (
-            
- {line.prompt && $ } - {line.children} -
- ))} -
-
-
- ); -} diff --git a/web/packages/ui/src/components/site-footer.tsx b/web/packages/ui/src/components/site-footer.tsx index 31536aa14..4e4064ceb 100644 --- a/web/packages/ui/src/components/site-footer.tsx +++ b/web/packages/ui/src/components/site-footer.tsx @@ -26,7 +26,6 @@ const defaultColumns: FooterColumn[] = [ { label: "Pricing", href: "/pricing" }, { label: "Docs", href: "/docs" }, { label: "Enterprise", href: "https://enterprise.nativelink.com" }, - { label: "Status", href: "/status" }, ], }, { @@ -167,16 +166,6 @@ export function SiteFooter({ ))} - - - - - - All systems operational - From 8b7439435577a9465ced7229f16664438b607cd3 Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Mon, 29 Jun 2026 08:49:35 -0600 Subject: [PATCH 009/144] Add Oracle store (#2492) --- nativelink-config/README.md | 57 ++++++ nativelink-config/examples/oci_backend.json5 | 186 ++++++++++++++++++ nativelink-config/src/stores.rs | 48 +++++ nativelink-store/BUILD.bazel | 2 + nativelink-store/src/default_store_factory.rs | 4 + nativelink-store/src/lib.rs | 1 + nativelink-store/src/oci_store.rs | 120 +++++++++++ nativelink-store/tests/oci_store_test.rs | 59 ++++++ 8 files changed, 477 insertions(+) create mode 100644 nativelink-config/examples/oci_backend.json5 create mode 100644 nativelink-store/src/oci_store.rs create mode 100644 nativelink-store/tests/oci_store_test.rs diff --git a/nativelink-config/README.md b/nativelink-config/README.md index 6c8856a16..9d5a39861 100644 --- a/nativelink-config/README.md +++ b/nativelink-config/README.md @@ -281,6 +281,63 @@ If `access_key_id` and `secret_access_key` are omitted, NativeLink falls back to the standard AWS credential chain (`AWS_*` env vars, `~/.aws/credentials`, IMDS). +### OCI Store + +[Oracle Cloud Infrastructure Object Storage](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm) +exposes an S3-compatible API, so NativeLink can use it as a CAS/AC backend. The +path-style endpoint is derived from your Object Storage `namespace` and +`region` as +`https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com`. +Authenticate with a Customer Secret Key (an Access Key/Secret Key pair created +under **User Settings → Customer secret keys** in the OCI console); the secret +cannot be retrieved after generation, so read it from an env var via +`shellexpand`. + +```js +{ + "stores": [ + { + "name": "CAS_MAIN_STORE", + "experimental_cloud_object_store": { + "provider": "oci", + "namespace": "your-object-storage-namespace", + "region": "us-phoenix-1", + "bucket": "nativelink-cas", + "access_key_id": "${OCI_ACCESS_KEY_ID}", + "secret_access_key": "${OCI_SECRET_ACCESS_KEY}", + "key_prefix": "cas/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5, + } + } + }, + { + "name": "AC_MAIN_STORE", + "experimental_cloud_object_store": { + "provider": "oci", + "namespace": "your-object-storage-namespace", + "region": "us-phoenix-1", + "bucket": "nativelink-cas", + "access_key_id": "${OCI_ACCESS_KEY_ID}", + "secret_access_key": "${OCI_SECRET_ACCESS_KEY}", + "key_prefix": "ac/", + } + } + ], + // Place rest of configuration here ... +} +``` + +A complete runnable example with CAS and AC is at +[`nativelink-config/examples/oci_backend.json5`](https://github.com/TraceMachina/nativelink/blob/main/nativelink-config/examples/oci_backend.json5). +The `region` is used both to build the endpoint host and as the AWS `SigV4` +signing region; if your tooling cannot set an OCI region identifier, OCI also +accepts `us-east-1` to target your tenancy home region. As with the other +S3-compatible stores, omitting `access_key_id` and `secret_access_key` falls +back to the standard AWS credential chain (`AWS_*` env vars). + ### Fast Slow Store This store will first attempt to read from the `fast` store when reading and if diff --git a/nativelink-config/examples/oci_backend.json5 b/nativelink-config/examples/oci_backend.json5 new file mode 100644 index 000000000..496233ad0 --- /dev/null +++ b/nativelink-config/examples/oci_backend.json5 @@ -0,0 +1,186 @@ +// Oracle Cloud Infrastructure (OCI) Object Storage backend via the Amazon S3 +// Compatibility API. +// +// The endpoint is derived from `namespace` + `region` as +// https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com and is +// always addressed path-style (bucket as the first path segment). +// +// Replace the access_key_id / secret_access_key placeholders below with a +// Customer Secret Key (created under User Settings -> Customer secret keys), or +// with shellexpand refs like "${OCI_ACCESS_KEY_ID}". You can also omit both +// placeholders to fall back to the AWS default credential chain (but it only +// reads AWS_* env var names -- your OCI keys would need to live there). +{ + stores: [ + { + name: "CAS_MAIN_STORE", + verify: { + verify_size: true, + backend: { + dedup: { + index_store: { + fast_slow: { + fast: { + memory: { + eviction_policy: { + max_bytes: "100mb", + }, + }, + }, + slow: { + experimental_cloud_object_store: { + provider: "oci", + namespace: "axaxnpcrorw5", + region: "us-phoenix-1", + bucket: "nativelink-cas-test", + access_key_id: "oci_access_key_id", + secret_access_key: "oci_secret_access_key", + key_prefix: "cas-index/", + retry: { + max_retries: 6, + delay: 0.3, + jitter: 0.5, + }, + }, + }, + }, + }, + content_store: { + compression: { + compression_algorithm: { + lz4: {}, + }, + backend: { + fast_slow: { + fast: { + memory: { + eviction_policy: { + max_bytes: "100mb", + }, + }, + }, + slow: { + experimental_cloud_object_store: { + provider: "oci", + namespace: "axaxnpcrorw5", + region: "us-phoenix-1", + bucket: "nativelink-cas-test", + access_key_id: "oci_access_key_id", + secret_access_key: "oci_secret_access_key", + key_prefix: "cas/", + retry: { + max_retries: 6, + delay: 0.3, + jitter: 0.5, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "AC_MAIN_STORE", + fast_slow: { + fast: { + memory: { + eviction_policy: { + max_bytes: "100mb", + }, + }, + }, + slow: { + experimental_cloud_object_store: { + provider: "oci", + namespace: "axaxnpcrorw5", + region: "us-phoenix-1", + bucket: "nativelink-cas-test", + access_key_id: "oci_access_key_id", + secret_access_key: "oci_secret_access_key", + key_prefix: "ac/", + retry: { + max_retries: 6, + delay: 0.3, + jitter: 0.5, + }, + }, + }, + }, + }, + ], + schedulers: [ + { + name: "MAIN_SCHEDULER", + simple: { + supported_platform_properties: { + cpu_count: "minimum", + memory_kb: "minimum", + network_kbps: "minimum", + disk_read_iops: "minimum", + disk_read_bps: "minimum", + disk_write_iops: "minimum", + disk_write_bps: "minimum", + shm_size: "minimum", + gpu_count: "minimum", + gpu_model: "exact", + cpu_vendor: "exact", + cpu_arch: "exact", + cpu_model: "exact", + kernel_version: "exact", + docker_image: "priority", + "lre-rs": "priority", + ISA: "exact", + }, + }, + }, + ], + servers: [ + { + listener: { + http: { + socket_address: "0.0.0.0:50051", + }, + }, + services: { + cas: [ + { + instance_name: "main", + cas_store: "CAS_MAIN_STORE", + }, + ], + ac: [ + { + instance_name: "main", + ac_store: "AC_MAIN_STORE", + }, + ], + execution: [ + { + instance_name: "main", + cas_store: "CAS_MAIN_STORE", + scheduler: "MAIN_SCHEDULER", + }, + ], + capabilities: [ + { + instance_name: "main", + remote_execution: { + scheduler: "MAIN_SCHEDULER", + }, + }, + ], + bytestream: [ + { + instance_name: "main", + cas_store: "CAS_MAIN_STORE", + }, + ], + health: {}, + }, + }, + ], +} diff --git a/nativelink-config/src/stores.rs b/nativelink-config/src/stores.rs index d8b7f473d..9cc0aff1b 100644 --- a/nativelink-config/src/stores.rs +++ b/nativelink-config/src/stores.rs @@ -747,6 +747,53 @@ pub struct ExperimentalR2Spec { pub common: CommonObjectSpec, } +// Oracle Cloud Infrastructure (OCI) Object Storage Spec. +// +// Uses the OCI Object Storage Amazon S3 Compatibility API. The store talks to +// the path-style compatibility endpoint, which embeds the Object Storage +// namespace in the host and the bucket in the request path: +// `https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com/{bucket}/{object}`. +// Authentication uses a Customer Secret Key (an Access Key/Secret Key pair +// generated under User Settings in the OCI console) signed with AWS SigV4. +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[serde(deny_unknown_fields)] +#[cfg_attr(feature = "dev-schema", derive(JsonSchema))] +pub struct ExperimentalOciSpec { + /// OCI Object Storage namespace. This is the immutable, system-generated + /// top-level container assigned to the tenancy (the same name in every + /// region). It is the host prefix of the derived path-style endpoint: + /// `https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com`. + #[serde(deserialize_with = "convert_string_with_shellexpand")] + pub namespace: String, + + /// OCI region identifier, for example `us-phoenix-1` or `us-ashburn-1`. + /// Used both to build the endpoint host and as the AWS `SigV4` signing + /// region. If your tooling cannot set an OCI region identifier, OCI also + /// accepts `us-east-1` to target the tenancy home region. + #[serde(deserialize_with = "convert_string_with_shellexpand")] + pub region: String, + + /// Bucket name to use as the backend. Bucket names must be unique within + /// the Object Storage namespace. + #[serde(deserialize_with = "convert_string_with_shellexpand")] + pub bucket: String, + + /// Customer Secret Key access key. When omitted (along with + /// `secret_access_key`), the default AWS credential chain is used instead. + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub access_key_id: Option, + + /// Customer Secret Key secret. OCI does not allow retrieving a secret key + /// after generation, so store it securely (for example via `${ENV_VAR}` + /// shell expansion). + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub secret_access_key: Option, + + /// Retry and upload settings. + #[serde(flatten)] + pub common: CommonObjectSpec, +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] #[cfg_attr(feature = "dev-schema", derive(JsonSchema))] @@ -1029,6 +1076,7 @@ pub enum ExperimentalCloudObjectSpec { Azure(ExperimentalAzureSpec), Ontap(ExperimentalOntapS3Spec), R2(ExperimentalR2Spec), + Oci(ExperimentalOciSpec), } impl Default for ExperimentalCloudObjectSpec { diff --git a/nativelink-store/BUILD.bazel b/nativelink-store/BUILD.bazel index 0911171ad..225ca38cd 100644 --- a/nativelink-store/BUILD.bazel +++ b/nativelink-store/BUILD.bazel @@ -33,6 +33,7 @@ rust_library( "src/memory_store.rs", "src/mongo_store.rs", "src/noop_store.rs", + "src/oci_store.rs", "src/ontap_s3_existence_cache_store.rs", "src/ontap_s3_store.rs", "src/r2_store.rs", @@ -130,6 +131,7 @@ rust_test_suite( "tests/grpc_store_test.rs", "tests/memory_store_test.rs", "tests/mongo_store_test.rs", + "tests/oci_store_test.rs", "tests/ontap_s3_existence_cache_store_test.rs", "tests/ontap_s3_store_test.rs", "tests/r2_store_test.rs", diff --git a/nativelink-store/src/default_store_factory.rs b/nativelink-store/src/default_store_factory.rs index 6dfdbde3e..01ae9d064 100644 --- a/nativelink-store/src/default_store_factory.rs +++ b/nativelink-store/src/default_store_factory.rs @@ -36,6 +36,7 @@ use crate::grpc_store::GrpcStore; use crate::memory_store::MemoryStore; use crate::mongo_store::ExperimentalMongoStore; use crate::noop_store::NoopStore; +use crate::oci_store::OciStore; use crate::ontap_s3_existence_cache_store::OntapS3ExistenceCache; use crate::ontap_s3_store::OntapS3Store; use crate::r2_store::R2Store; @@ -77,6 +78,9 @@ pub fn store_factory<'a>( ExperimentalCloudObjectSpec::R2(r2_config) => { R2Store::new(r2_config, SystemTime::now).await? } + ExperimentalCloudObjectSpec::Oci(oci_config) => { + OciStore::new(oci_config, SystemTime::now).await? + } }, StoreSpec::RedisStore(spec) => { if spec.mode == RedisMode::Cluster { diff --git a/nativelink-store/src/lib.rs b/nativelink-store/src/lib.rs index 5fc13fccd..1ceea189a 100644 --- a/nativelink-store/src/lib.rs +++ b/nativelink-store/src/lib.rs @@ -31,6 +31,7 @@ pub mod grpc_store; pub mod memory_store; pub mod mongo_store; pub mod noop_store; +pub mod oci_store; pub mod ontap_s3_existence_cache_store; pub mod ontap_s3_store; pub mod r2_store; diff --git a/nativelink-store/src/oci_store.rs b/nativelink-store/src/oci_store.rs new file mode 100644 index 000000000..942c419cd --- /dev/null +++ b/nativelink-store/src/oci_store.rs @@ -0,0 +1,120 @@ +// Copyright 2026 The NativeLink Authors. All rights reserved. +// +// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// See LICENSE file for details +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::time::Duration; +use std::borrow::Cow; +use std::sync::Arc; + +use aws_config::default_provider::credentials::DefaultCredentialsChain; +use aws_config::provider_config::ProviderConfig; +use aws_config::{AppName, BehaviorVersion}; +use aws_sdk_s3::Client; +use aws_sdk_s3::config::{Credentials, Region}; +use nativelink_config::stores::{ExperimentalAwsSpec, ExperimentalOciSpec}; +use nativelink_error::Error; +use nativelink_util::instant_wrapper::InstantWrapper; + +use crate::common_s3_utils::TlsClient; +use crate::s3_store::S3Store; + +/// OCI-shaped config adapter over [`S3Store`]. Builds an S3 SDK client pointed +/// at Oracle Cloud Infrastructure Object Storage via its Amazon S3 +/// Compatibility API and hands it to `S3Store::new_with_client_and_jitter`. +/// +/// The compatibility API requires path-style addressing: the namespace lives in +/// the host and the bucket is the first path segment +/// (`https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com/{bucket}/{object}`), +/// so `force_path_style(true)` is set on the client. Requests are signed with +/// AWS `SigV4` using the OCI region as the signing region; credentials come from +/// a Customer Secret Key when supplied, otherwise the default AWS credential +/// chain. +#[derive(Debug, Clone, Copy)] +pub struct OciStore; + +impl OciStore { + #[allow(clippy::new_ret_no_self)] // Because usually everyone returns themselves + pub async fn new( + spec: &ExperimentalOciSpec, + now_fn: NowFn, + ) -> Result>, Error> + where + I: InstantWrapper, + NowFn: Fn() -> I + Send + Sync + Unpin + 'static, + { + let aws_spec = Self::build_aws_spec(spec); + let jitter_fn = spec.common.retry.make_jitter_fn(); + + let http_client = TlsClient::new(&spec.common.clone()); + let endpoint = Self::derive_endpoint(spec); + let region = Region::new(Cow::Owned(spec.region.clone())); + + let mut config_builder = aws_sdk_s3::Config::builder() + .behavior_version(BehaviorVersion::latest()) + .app_name(AppName::new("nativelink").expect("valid app name")) + .timeout_config( + aws_config::timeout::TimeoutConfig::builder() + .connect_timeout(Duration::from_secs(15)) + .build(), + ) + .region(region.clone()) + .endpoint_url(&endpoint) + // OCI's compatibility endpoint embeds the bucket in the path, not as + // a subdomain, so virtual-hosted addressing must be disabled. + .force_path_style(true) + .http_client(http_client.clone()); + + config_builder = if let Some(key_id) = &spec.access_key_id + && let Some(secret) = &spec.secret_access_key + { + config_builder.credentials_provider(Credentials::new( + key_id, + secret, + None, + None, + "oci-customer-secret-key", + )) + } else { + let default_chain = DefaultCredentialsChain::builder() + .configure( + ProviderConfig::without_region() + .with_region(Some(region)) + .with_http_client(http_client), + ) + .build() + .await; + config_builder.credentials_provider(default_chain) + }; + + let s3_client = Client::from_conf(config_builder.build()); + + S3Store::new_with_client_and_jitter(&aws_spec, s3_client, jitter_fn, now_fn) + } + + /// Derives the OCI Object Storage path-style S3-compatibility endpoint from + /// the namespace and region. + pub fn derive_endpoint(spec: &ExperimentalOciSpec) -> String { + format!( + "https://{}.compat.objectstorage.{}.oci.customer-oci.com", + spec.namespace, spec.region + ) + } + + pub fn build_aws_spec(spec: &ExperimentalOciSpec) -> ExperimentalAwsSpec { + ExperimentalAwsSpec { + region: spec.region.clone(), + bucket: spec.bucket.clone(), + common: spec.common.clone(), + } + } +} diff --git a/nativelink-store/tests/oci_store_test.rs b/nativelink-store/tests/oci_store_test.rs new file mode 100644 index 000000000..16f1c8f8e --- /dev/null +++ b/nativelink-store/tests/oci_store_test.rs @@ -0,0 +1,59 @@ +// Copyright 2026 The NativeLink Authors. All rights reserved. +// +// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// See LICENSE file for details +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! `OciStore` builds an SDK client and hands it to `S3Store`. The S3 wire +//! behaviour is covered by `s3_store_test.rs`. + +use nativelink_config::stores::{CommonObjectSpec, ExperimentalOciSpec}; +use nativelink_error::Error; +use nativelink_macro::nativelink_test; +use nativelink_store::oci_store::OciStore; +use pretty_assertions::assert_eq; + +#[nativelink_test] +async fn endpoint_is_derived_from_namespace_and_region() -> Result<(), Error> { + let endpoint = OciStore::derive_endpoint(&ExperimentalOciSpec { + namespace: "axaxnpcrorw5".to_string(), + region: "us-phoenix-1".to_string(), + bucket: "ignored".to_string(), + ..Default::default() + }); + assert_eq!( + endpoint, + "https://axaxnpcrorw5.compat.objectstorage.us-phoenix-1.oci.customer-oci.com", + ); + Ok(()) +} + +#[nativelink_test] +async fn aws_spec_passes_region_bucket_and_common_through() -> Result<(), Error> { + let aws_spec = OciStore::build_aws_spec(&ExperimentalOciSpec { + namespace: "ignored".to_string(), + region: "us-ashburn-1".to_string(), + bucket: "test-bucket".to_string(), + common: CommonObjectSpec { + key_prefix: Some("cas/".to_string()), + consider_expired_after_s: 86400, + ..Default::default() + }, + ..Default::default() + }); + // The OCI region is forwarded verbatim so it is used as the SigV4 signing + // region (unlike R2, which always signs against `auto`). + assert_eq!(aws_spec.region, "us-ashburn-1"); + assert_eq!(aws_spec.bucket, "test-bucket"); + assert_eq!(aws_spec.common.key_prefix.as_deref(), Some("cas/")); + assert_eq!(aws_spec.common.consider_expired_after_s, 86400); + Ok(()) +} From ce3a919ecce0e6f4ac3ebede5e90813b16f888f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:35:23 +0000 Subject: [PATCH 010/144] Update Rust crate opentelemetry_sdk to 0.32.0 [SECURITY] (#2487) * Update Rust crate opentelemetry_sdk to 0.32.0 [SECURITY] * Upgrade all the other opentelemetry bits * Upgrade Tonic to 0.14 throughout --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Tom Parker-Shemilt --- Cargo.lock | 126 +- Cargo.toml | 3 +- MODULE.bazel.lock | 34 +- nativelink-error/BUILD.bazel | 1 + nativelink-error/Cargo.toml | 7 +- nativelink-error/src/lib.rs | 6 + nativelink-error/tests/lib_tests.rs | 2 +- nativelink-proto/BUILD.bazel | 4 +- nativelink-proto/Cargo.toml | 14 +- nativelink-proto/gen_protos_tool.rs | 6 +- .../genproto/blaze.invocation_policy.pb.rs | 16 +- nativelink-proto/genproto/blaze.pb.rs | 2 +- .../genproto/blaze.strategy_policy.pb.rs | 2 +- .../build.bazel.remote.asset.v1.pb.rs | 364 +-- .../build.bazel.remote.execution.v2.pb.rs | 1962 ++++++++--------- .../genproto/build.bazel.semver.pb.rs | 2 +- .../genproto/build_event_stream.pb.rs | 126 +- ...thub.trace_machina.nativelink.events.pb.rs | 8 +- ..._machina.nativelink.remote_execution.pb.rs | 16 +- nativelink-proto/genproto/command_line.pb.rs | 4 +- .../devtools.build.lib.packages.metrics.pb.rs | 2 +- .../genproto/failure_details.pb.rs | 144 +- nativelink-proto/genproto/google.api.pb.rs | 4 +- .../genproto/google.bytestream.pb.rs | 24 +- .../genproto/google.devtools.build.v1.pb.rs | 38 +- .../genproto/google.longrunning.pb.rs | 32 +- nativelink-proto/genproto/google.rpc.pb.rs | 16 +- nativelink-scheduler/Cargo.toml | 8 +- nativelink-service/BUILD.bazel | 1 + nativelink-service/Cargo.toml | 12 +- nativelink-service/tests/bep_server_test.rs | 3 +- .../tests/bytestream_server_test.rs | 5 +- nativelink-store/Cargo.toml | 6 +- nativelink-test/fuzz/Cargo.lock | 51 +- nativelink-util/BUILD.bazel | 1 + nativelink-util/Cargo.toml | 27 +- nativelink-util/src/origin_event.rs | 9 +- nativelink-util/tests/telemetry_test.rs | 3 +- nativelink-worker/BUILD.bazel | 1 + nativelink-worker/Cargo.toml | 9 +- .../tests/utils/local_worker_test_utils.rs | 2 +- 41 files changed, 1585 insertions(+), 1518 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2882eb11d..8590d5a1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1988,8 +1988,7 @@ dependencies = [ [[package]] name = "ginepro" version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b4db47d89cd1f347167b2704df3eeb3ffe1b9afe643fa768a5f8464581921f" +source = "git+https://github.com/mstyura/ginepro?rev=d08cdeff6300edfb46204b3b9fbde3f3355db35f#d08cdeff6300edfb46204b3b9fbde3f3355db35f" dependencies = [ "anyhow", "async-trait", @@ -2350,7 +2349,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "tokio", "tower-service", "tracing", @@ -3103,6 +3102,7 @@ dependencies = [ name = "nativelink-error" version = "1.5.2" dependencies = [ + "base64 0.22.1", "mongodb", "nativelink-metric", "nativelink-proto", @@ -3157,10 +3157,10 @@ version = "1.5.2" dependencies = [ "derive_more 2.1.0", "prost", - "prost-build", "prost-types", "tonic", - "tonic-build", + "tonic-prost", + "tonic-prost-build", ] [[package]] @@ -3247,6 +3247,7 @@ dependencies = [ "tokio", "tokio-stream", "tonic", + "tonic-prost", "tower", "tracing", "tracing-test", @@ -3380,6 +3381,7 @@ dependencies = [ "tokio-stream", "tokio-util", "tonic", + "tonic-prost", "tower", "tracing", "tracing-opentelemetry", @@ -3427,6 +3429,7 @@ dependencies = [ "tokio", "tokio-stream", "tonic", + "tonic-prost", "tracing", "tracing-test", "uuid", @@ -3555,23 +3558,22 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "opentelemetry" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf416e4cb72756655126f7dd7bb0af49c674f4c1b9903e80c009e0c37e552e6" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" dependencies = [ "futures-core", "futures-sink", "js-sys", "pin-project-lite", "thiserror 2.0.18", - "tracing", ] [[package]] name = "opentelemetry-appender-tracing" -version = "0.30.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68f63eca5fad47e570e00e893094fc17be959c80c79a7d6ec1abdd5ae6ffc16" +checksum = "2c0080f0dc1d7c786f467cd85a4e395fcab11ee852004f39a29a18ab7c25d837" dependencies = [ "opentelemetry", "tracing", @@ -3581,9 +3583,9 @@ dependencies = [ [[package]] name = "opentelemetry-http" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d" +checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" dependencies = [ "async-trait", "bytes", @@ -3593,9 +3595,9 @@ dependencies = [ [[package]] name = "opentelemetry-otlp" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b" +checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" dependencies = [ "http 1.4.1", "opentelemetry", @@ -3605,39 +3607,41 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tonic", + "tonic-types", ] [[package]] name = "opentelemetry-proto" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc" +checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" dependencies = [ "opentelemetry", "opentelemetry_sdk", "prost", "tonic", + "tonic-prost", ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.30.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d059a296a47436748557a353c5e6c5705b9470ef6c95cfc52c21a8814ddac2" +checksum = "c913ac17a6c451661ee255f4625d143e51647ae78ebd969b75e41c4442f4fe47" [[package]] name = "opentelemetry_sdk" -version = "0.30.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f644aa9e5e31d11896e024305d7e3c98a88884d9f8919dbf37a9991bc47a4b" +checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" dependencies = [ "futures-channel", "futures-executor", "futures-util", "opentelemetry", "percent-encoding", + "portable-atomic", "rand 0.9.4", - "serde_json", "thiserror 2.0.18", ] @@ -3812,11 +3816,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.7.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", + "hashbrown 0.15.5", "indexmap 2.14.0", ] @@ -3960,9 +3965,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -3970,15 +3975,14 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck", "itertools", "log", "multimap", - "once_cell", "petgraph", "prettyplease", "prost", @@ -3990,9 +3994,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools", @@ -4003,9 +4007,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] @@ -4033,7 +4037,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -4070,7 +4074,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", "windows-sys 0.60.2", ] @@ -5413,9 +5417,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.13.1" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum", @@ -5431,9 +5435,9 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "prost", "rustls-native-certs", - "socket2 0.5.10", + "socket2 0.6.4", + "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", @@ -5446,9 +5450,32 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.13.1" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac6f67be712d12f0b41328db3137e0d0757645d8904b4cb7d51cd9c2279e847" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" dependencies = [ "prettyplease", "proc-macro2", @@ -5456,6 +5483,19 @@ dependencies = [ "prost-types", "quote", "syn", + "tempfile", + "tonic-build", +] + +[[package]] +name = "tonic-types" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab1b02061f83d519bba3caa167f88f261ef05720ab8ebc954ade70de3348e8" +dependencies = [ + "prost", + "prost-types", + "tonic", ] [[package]] @@ -5552,14 +5592,12 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddcf5959f39507d0d04d6413119c04f33b623f4f951ebcbdddddfad2d0623a9c" +checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" dependencies = [ "js-sys", - "once_cell", "opentelemetry", - "opentelemetry_sdk", "smallvec", "tracing", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index 9cf585de2..eeba7a5dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,7 @@ tokio = { version = "1.52.2", features = [ tokio-rustls = { version = "0.26.2", default-features = false, features = [ "ring", ] } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "tls-ring", "transport", ], default-features = false } @@ -94,7 +94,6 @@ serial_test = ["async"] tokio = ["fs", "io-util", "rt-multi-thread", "signal"] tokio-stream = ["fs"] tonic = ["tls", "transport"] -tonic-build = ["prost"] uuid = ["serde", "v4"] [workspace.lints.rust] diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 74cd70646..7a51eee11 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -967,7 +967,7 @@ "getrandom_0.2.16": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"wasi\",\"req\":\"^0.11\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.18\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"}],\"features\":{\"custom\":[],\"js\":[\"wasm-bindgen\",\"js-sys\"],\"linux_disable_fallback\":[],\"rdrand\":[],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\",\"libc/rustc-dep-of-std\",\"wasi/rustc-dep-of-std\"],\"std\":[],\"test-in-browser\":[]}}", "getrandom_0.3.4": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), not(any(all(target_os = \\\"linux\\\", target_env = \\\"\\\"), getrandom_backend = \\\"custom\\\", getrandom_backend = \\\"linux_raw\\\", getrandom_backend = \\\"rdrand\\\", getrandom_backend = \\\"rndr\\\"))))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"hurd\\\", target_os = \\\"illumos\\\", target_os = \\\"cygwin\\\", all(target_os = \\\"horizon\\\", target_arch = \\\"arm\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"haiku\\\", target_os = \\\"redox\\\", target_os = \\\"nto\\\", target_os = \\\"aix\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\", target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"openbsd\\\", target_os = \\\"vita\\\", target_os = \\\"emscripten\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"netbsd\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"solaris\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"vxworks\\\")\"},{\"default_features\":false,\"name\":\"r-efi\",\"req\":\"^5.1\",\"target\":\"cfg(all(target_os = \\\"uefi\\\", getrandom_backend = \\\"efi_rng\\\"))\"},{\"default_features\":false,\"name\":\"wasip2\",\"req\":\"^1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p2\\\"))\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"}],\"features\":{\"std\":[],\"wasm_js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"]}}", "getrandom_0.4.2": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), not(any(all(target_os = \\\"linux\\\", target_env = \\\"\\\"), getrandom_backend = \\\"custom\\\", getrandom_backend = \\\"linux_raw\\\", getrandom_backend = \\\"rdrand\\\", getrandom_backend = \\\"rndr\\\"))))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"hurd\\\", target_os = \\\"illumos\\\", target_os = \\\"cygwin\\\", all(target_os = \\\"horizon\\\", target_arch = \\\"arm\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"haiku\\\", target_os = \\\"redox\\\", target_os = \\\"nto\\\", target_os = \\\"aix\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\", target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"openbsd\\\", target_os = \\\"vita\\\", target_os = \\\"emscripten\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"netbsd\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"solaris\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"vxworks\\\")\"},{\"default_features\":false,\"name\":\"r-efi\",\"req\":\"^6\",\"target\":\"cfg(all(target_os = \\\"uefi\\\", getrandom_backend = \\\"efi_rng\\\"))\"},{\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.10.0\"},{\"default_features\":false,\"name\":\"wasip2\",\"req\":\"^1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p2\\\"))\"},{\"name\":\"wasip3\",\"req\":\"^0.4\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p3\\\"))\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"}],\"features\":{\"std\":[],\"sys_rng\":[\"dep:rand_core\"],\"wasm_js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"]}}", - "ginepro_0.9.3": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1\"},{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"features\":[\"tokio\"],\"name\":\"hickory-resolver\",\"req\":\"^0.26\"},{\"name\":\"http\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"full\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"router\",\"transport\",\"codegen\",\"prost\"],\"name\":\"tonic\",\"req\":\"^0.13\"},{\"default_features\":false,\"features\":[\"discover\"],\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tracing\",\"req\":\"^0.1\"}],\"features\":{\"default\":[\"tls-ring\"],\"tls-aws-lc\":[\"tonic/tls-aws-lc\"],\"tls-ring\":[\"tonic/tls-ring\"]}}", + "git+https://github.com/mstyura/ginepro?rev=d08cdeff6300edfb46204b3b9fbde3f3355db35f#d08cdeff6300edfb46204b3b9fbde3f3355db35f_ginepro": "{\"dependencies\":[{\"name\":\"anyhow\"},{\"name\":\"async-trait\"},{\"default_features\":true,\"features\":[\"tokio\"],\"name\":\"hickory-resolver\",\"optional\":false},{\"name\":\"http\"},{\"name\":\"thiserror\"},{\"default_features\":true,\"features\":[\"full\"],\"name\":\"tokio\",\"optional\":false},{\"default_features\":false,\"features\":[\"router\",\"transport\",\"codegen\"],\"name\":\"tonic\",\"optional\":false},{\"default_features\":false,\"features\":[\"discover\"],\"name\":\"tower\",\"optional\":false},{\"name\":\"tracing\"}],\"features\":{\"default\":[\"tls-ring\"],\"tls-aws-lc\":[\"tonic/tls-aws-lc\"],\"tls-ring\":[\"tonic/tls-ring\"]},\"strip_prefix\":\"ginepro\"}", "goblin_0.10.5": "{\"dependencies\":[{\"default_features\":false,\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"plain\",\"req\":\"^0.2.3\"},{\"default_features\":false,\"name\":\"scroll\",\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"stderrlog\",\"req\":\"^0.6.0\"}],\"features\":{\"alloc\":[\"scroll/derive\",\"log\"],\"archive\":[\"alloc\"],\"default\":[\"std\",\"elf32\",\"elf64\",\"mach32\",\"mach64\",\"pe32\",\"pe64\",\"te\",\"archive\",\"endian_fd\"],\"elf32\":[],\"elf64\":[],\"endian_fd\":[\"alloc\"],\"mach32\":[\"alloc\",\"endian_fd\",\"archive\"],\"mach64\":[\"alloc\",\"endian_fd\",\"archive\"],\"pe32\":[\"alloc\",\"endian_fd\"],\"pe64\":[\"alloc\",\"endian_fd\"],\"std\":[\"alloc\",\"scroll/std\"],\"te\":[\"alloc\",\"endian_fd\"]}}", "group_0.13.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"ff\",\"req\":\"^0.13\"},{\"name\":\"memuse\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"name\":\"rand_xorshift\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.2.1\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\"],\"tests\":[\"alloc\",\"rand\",\"rand_xorshift\"],\"wnaf-memuse\":[\"alloc\",\"memuse\"]}}", "h2_0.4.13": "{\"dependencies\":[{\"name\":\"atomic-waker\",\"req\":\"^1.0.0\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-sink\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"http\",\"req\":\"^1\"},{\"features\":[\"std\"],\"name\":\"indexmap\",\"req\":\"^2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.0\"},{\"name\":\"slab\",\"req\":\"^0.4.2\"},{\"features\":[\"io-util\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"sync\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-rustls\",\"req\":\"^0.26\"},{\"features\":[\"codec\",\"io\"],\"name\":\"tokio-util\",\"req\":\"^0.7.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^1\"}],\"features\":{\"stream\":[],\"unstable\":[]}}", @@ -1092,13 +1092,13 @@ "once_cell_1.21.4": "{\"dependencies\":[{\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1.1.3\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"critical-section\",\"req\":\"^1.1.3\"},{\"default_features\":false,\"name\":\"parking_lot_core\",\"optional\":true,\"req\":\"^0.9.10\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"}],\"features\":{\"alloc\":[\"race\"],\"atomic-polyfill\":[\"critical-section\"],\"critical-section\":[\"dep:critical-section\",\"portable-atomic\"],\"default\":[\"std\"],\"parking_lot\":[\"dep:parking_lot_core\"],\"portable-atomic\":[\"dep:portable-atomic\"],\"race\":[],\"std\":[\"alloc\"],\"unstable\":[]}}", "once_cell_polyfill_1.70.2": "{\"dependencies\":[],\"features\":{\"default\":[]}}", "openssl-probe_0.1.6": "{\"dependencies\":[],\"features\":{}}", - "opentelemetry-appender-tracing_0.30.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.21\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.21\"},{\"features\":[\"logs\"],\"name\":\"opentelemetry\",\"req\":\"^0.30\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"default_features\":false,\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\">=0.1.40\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\">=0.1.40\"},{\"default_features\":false,\"name\":\"tracing-core\",\"req\":\">=0.1.33\"},{\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"tracing-log\",\"req\":\"^0.2\"},{\"name\":\"tracing-opentelemetry\",\"optional\":true,\"req\":\"^0.31\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"env-filter\",\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"default\":[],\"experimental_metadata_attributes\":[\"dep:tracing-log\"],\"experimental_use_tracing_span_context\":[\"tracing-opentelemetry\"],\"spec_unstable_logs_enabled\":[\"opentelemetry/spec_unstable_logs_enabled\"]}}", - "opentelemetry-http_0.30.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"hyper\",\"optional\":true,\"req\":\"^1.3\"},{\"features\":[\"client-legacy\",\"http1\",\"http2\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.30\"},{\"default_features\":false,\"features\":[\"blocking\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[\"internal-logs\"],\"hyper\":[\"dep:http-body-util\",\"dep:hyper\",\"dep:hyper-util\",\"dep:tokio\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"reqwest-rustls\":[\"reqwest\",\"reqwest/rustls-tls-native-roots\"],\"reqwest-rustls-webpki-roots\":[\"reqwest\",\"reqwest/rustls-tls-webpki-roots\"]}}", - "opentelemetry-otlp_0.30.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"optional\":true,\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.30\"},{\"name\":\"opentelemetry-http\",\"optional\":true,\"req\":\"^0.30\"},{\"default_features\":false,\"name\":\"opentelemetry-proto\",\"req\":\"^0.30\"},{\"default_features\":false,\"name\":\"opentelemetry_sdk\",\"req\":\"^0.30\"},{\"name\":\"prost\",\"optional\":true,\"req\":\"^0.13\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"sync\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"net\"],\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"optional\":true,\"req\":\"^0.13\"},{\"default_features\":false,\"features\":[\"router\",\"server\"],\"kind\":\"dev\",\"name\":\"tonic\",\"req\":\"^0.13\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\">=0.1.40\"}],\"features\":{\"default\":[\"http-proto\",\"reqwest-blocking-client\",\"trace\",\"metrics\",\"logs\",\"internal-logs\"],\"grpc-tonic\":[\"tonic\",\"prost\",\"http\",\"tokio\",\"opentelemetry-proto/gen-tonic\"],\"gzip-tonic\":[\"tonic/gzip\"],\"http-json\":[\"serde_json\",\"prost\",\"opentelemetry-http\",\"opentelemetry-proto/gen-tonic-messages\",\"opentelemetry-proto/with-serde\",\"http\",\"trace\",\"metrics\"],\"http-proto\":[\"prost\",\"opentelemetry-http\",\"opentelemetry-proto/gen-tonic-messages\",\"http\",\"trace\",\"metrics\"],\"hyper-client\":[\"opentelemetry-http/hyper\"],\"integration-testing\":[\"tonic\",\"prost\",\"tokio/full\",\"trace\",\"logs\"],\"internal-logs\":[\"tracing\",\"opentelemetry/internal-logs\"],\"logs\":[\"opentelemetry/logs\",\"opentelemetry_sdk/logs\",\"opentelemetry-proto/logs\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\",\"opentelemetry-proto/metrics\"],\"reqwest-blocking-client\":[\"reqwest/blocking\",\"opentelemetry-http/reqwest\"],\"reqwest-client\":[\"reqwest\",\"opentelemetry-http/reqwest\"],\"reqwest-rustls\":[\"reqwest\",\"opentelemetry-http/reqwest-rustls\"],\"reqwest-rustls-webpki-roots\":[\"reqwest\",\"opentelemetry-http/reqwest-rustls-webpki-roots\"],\"serialize\":[\"serde\",\"serde_json\"],\"tls\":[\"tonic/tls-ring\"],\"tls-roots\":[\"tls\",\"tonic/tls-native-roots\"],\"tls-webpki-roots\":[\"tls\",\"tonic/tls-webpki-roots\"],\"trace\":[\"opentelemetry/trace\",\"opentelemetry_sdk/trace\",\"opentelemetry-proto/trace\"],\"zstd-tonic\":[\"tonic/zstd\"]}}", - "opentelemetry-proto_0.30.0": "{\"dependencies\":[{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4.3\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.30\"},{\"default_features\":false,\"name\":\"opentelemetry_sdk\",\"req\":\"^0.30\"},{\"name\":\"prost\",\"optional\":true,\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"prost-build\",\"req\":\"^0.13\"},{\"name\":\"schemars\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"serde_derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"},{\"default_features\":false,\"features\":[\"codegen\",\"prost\"],\"name\":\"tonic\",\"optional\":true,\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"tonic-build\",\"req\":\"^0.13\"}],\"features\":{\"default\":[\"full\"],\"full\":[\"gen-tonic\",\"trace\",\"logs\",\"metrics\",\"zpages\",\"with-serde\",\"internal-logs\"],\"gen-tonic\":[\"gen-tonic-messages\",\"tonic/channel\"],\"gen-tonic-messages\":[\"tonic\",\"prost\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"logs\":[\"opentelemetry/logs\",\"opentelemetry_sdk/logs\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\"],\"profiles\":[],\"testing\":[\"opentelemetry/testing\"],\"trace\":[\"opentelemetry/trace\",\"opentelemetry_sdk/trace\"],\"with-schemars\":[\"schemars\"],\"with-serde\":[\"serde\",\"hex\",\"base64\"],\"zpages\":[\"trace\"]}}", - "opentelemetry-semantic-conventions_0.30.0": "{\"dependencies\":[],\"features\":{\"default\":[],\"semconv_experimental\":[]}}", - "opentelemetry_0.30.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-sink\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"js-sys\",\"req\":\"^0.3.63\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"os_rng\",\"thread_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"thiserror\",\"optional\":true,\"req\":\"^2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\">=0.1.40\"}],\"features\":{\"default\":[\"trace\",\"metrics\",\"logs\",\"internal-logs\",\"futures\"],\"futures\":[\"futures-core\",\"futures-sink\",\"pin-project-lite\"],\"internal-logs\":[\"tracing\"],\"logs\":[],\"metrics\":[],\"spec_unstable_logs_enabled\":[\"logs\"],\"testing\":[\"trace\"],\"trace\":[\"futures\",\"thiserror\"]}}", - "opentelemetry_sdk_0.30.0": "{\"dependencies\":[{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"futures-channel\",\"req\":\"^0.3\"},{\"name\":\"futures-executor\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\",\"sink\",\"async-await-macro\"],\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"optional\":true,\"req\":\"^1.1\"},{\"name\":\"opentelemetry\",\"req\":\"^0.30\"},{\"name\":\"opentelemetry-http\",\"optional\":true,\"req\":\"^0.30\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.0\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"std_rng\",\"small_rng\",\"os_rng\",\"thread_rng\"],\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.23.0\"},{\"default_features\":false,\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"rt\",\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"url\",\"optional\":true,\"req\":\"^2.5\"}],\"features\":{\"default\":[\"trace\",\"metrics\",\"logs\",\"internal-logs\"],\"experimental_async_runtime\":[],\"experimental_logs_batch_log_processor_with_async_runtime\":[\"logs\",\"experimental_async_runtime\"],\"experimental_logs_concurrent_log_processor\":[\"logs\"],\"experimental_metrics_custom_reader\":[\"metrics\"],\"experimental_metrics_disable_name_validation\":[\"metrics\"],\"experimental_metrics_periodicreader_with_async_runtime\":[\"metrics\",\"experimental_async_runtime\"],\"experimental_trace_batch_span_processor_with_async_runtime\":[\"trace\",\"experimental_async_runtime\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"jaeger_remote_sampler\":[\"trace\",\"opentelemetry-http\",\"http\",\"serde\",\"serde_json\",\"url\",\"experimental_async_runtime\"],\"logs\":[\"opentelemetry/logs\",\"serde_json\"],\"metrics\":[\"opentelemetry/metrics\"],\"rt-tokio\":[\"tokio\",\"tokio-stream\",\"experimental_async_runtime\"],\"rt-tokio-current-thread\":[\"tokio\",\"tokio-stream\",\"experimental_async_runtime\"],\"spec_unstable_logs_enabled\":[\"logs\",\"opentelemetry/spec_unstable_logs_enabled\"],\"spec_unstable_metrics_views\":[\"metrics\"],\"testing\":[\"opentelemetry/testing\",\"trace\",\"metrics\",\"logs\",\"rt-tokio\",\"rt-tokio-current-thread\",\"tokio/macros\",\"tokio/rt-multi-thread\"],\"trace\":[\"opentelemetry/trace\",\"rand\",\"percent-encoding\"]}}", + "opentelemetry-appender-tracing_0.32.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.21\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.21\"},{\"default_features\":false,\"features\":[\"logs\"],\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"features\":[\"logs\",\"testing\",\"internal-logs\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.32\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\">=0.1.40\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\">=0.1.40\"},{\"default_features\":false,\"name\":\"tracing-core\",\"req\":\">=0.1.33\"},{\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"tracing-log\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"env-filter\",\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"bench_profiling\":[],\"default\":[],\"experimental_metadata_attributes\":[\"dep:tracing-log\"],\"experimental_span_attributes\":[]}}", + "opentelemetry-http_0.32.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"hyper\",\"optional\":true,\"req\":\"^1.3\"},{\"features\":[\"client-legacy\",\"http1\",\"http2\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.1\"},{\"default_features\":false,\"features\":[\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[\"internal-logs\"],\"hyper\":[\"dep:http-body-util\",\"dep:hyper\",\"dep:hyper-util\",\"dep:tokio\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"reqwest\":[\"dep:reqwest\"],\"reqwest-blocking\":[\"dep:reqwest\",\"reqwest/blocking\"],\"reqwest-rustls\":[\"dep:reqwest\",\"reqwest/default-tls\"],\"reqwest-rustls-webpki-roots\":[\"dep:reqwest\",\"reqwest/default-tls\",\"reqwest/webpki-roots\"]}}", + "opentelemetry-otlp_0.32.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.1.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"optional\":true,\"req\":\"^1.1\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0.3\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"name\":\"opentelemetry-http\",\"optional\":true,\"req\":\"^0.32\"},{\"default_features\":false,\"name\":\"opentelemetry-proto\",\"req\":\"^0.32\"},{\"default_features\":false,\"name\":\"opentelemetry_sdk\",\"req\":\"^0.32\"},{\"default_features\":false,\"features\":[\"trace\",\"testing\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.32\"},{\"name\":\"prost\",\"optional\":true,\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"sync\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"net\"],\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"optional\":true,\"req\":\"^0.14.1\"},{\"default_features\":false,\"features\":[\"router\",\"server\"],\"kind\":\"dev\",\"name\":\"tonic\",\"req\":\"^0.14.1\"},{\"name\":\"tonic-types\",\"optional\":true,\"req\":\"^0.14.1\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13\"}],\"features\":{\"default\":[\"http-proto\",\"reqwest-blocking-client\",\"trace\",\"metrics\",\"logs\",\"internal-logs\"],\"experimental-grpc-retry\":[\"grpc-tonic\",\"opentelemetry_sdk/experimental_async_runtime\",\"opentelemetry_sdk/rt-tokio\"],\"experimental-http-retry\":[\"opentelemetry_sdk/experimental_async_runtime\",\"opentelemetry_sdk/rt-tokio\",\"tokio\",\"httpdate\"],\"grpc-tonic\":[\"tonic\",\"tonic-types\",\"prost\",\"http\",\"tokio\",\"opentelemetry-proto/gen-tonic\"],\"gzip-http\":[\"flate2\"],\"gzip-tonic\":[\"tonic/gzip\"],\"http-json\":[\"serde_json\",\"prost\",\"opentelemetry-http\",\"opentelemetry-proto/gen-tonic-messages\",\"opentelemetry-proto/with-serde\",\"http\",\"trace\",\"metrics\"],\"http-proto\":[\"prost\",\"opentelemetry-http\",\"opentelemetry-proto/gen-tonic-messages\",\"http\",\"trace\",\"metrics\"],\"hyper-client\":[\"opentelemetry-http/hyper\"],\"integration-testing\":[\"tonic\",\"prost\",\"tokio/full\",\"trace\",\"logs\"],\"internal-logs\":[\"opentelemetry_sdk/internal-logs\",\"opentelemetry/internal-logs\"],\"logs\":[\"opentelemetry/logs\",\"opentelemetry_sdk/logs\",\"opentelemetry-proto/logs\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\",\"opentelemetry-proto/metrics\"],\"reqwest-blocking-client\":[\"reqwest/blocking\",\"opentelemetry-http/reqwest-blocking\"],\"reqwest-client\":[\"reqwest\",\"opentelemetry-http/reqwest\"],\"reqwest-rustls\":[\"reqwest\",\"opentelemetry-http/reqwest-rustls\"],\"reqwest-rustls-webpki-roots\":[\"reqwest\",\"opentelemetry-http/reqwest-rustls-webpki-roots\"],\"serialize\":[\"serde\",\"serde_json\"],\"tls\":[\"tls-ring\"],\"tls-aws-lc\":[\"tonic/tls-aws-lc\"],\"tls-provider-agnostic\":[\"tonic/_tls-any\"],\"tls-ring\":[\"tonic/tls-ring\"],\"tls-roots\":[\"tonic/tls-native-roots\"],\"tls-webpki-roots\":[\"tonic/tls-webpki-roots\"],\"trace\":[\"opentelemetry/trace\",\"opentelemetry_sdk/trace\",\"opentelemetry-proto/trace\"],\"zstd-http\":[\"zstd\"],\"zstd-tonic\":[\"tonic/zstd\"]}}", + "opentelemetry-proto_0.32.0": "{\"dependencies\":[{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"name\":\"const-hex\",\"optional\":true,\"req\":\"^1.14.1\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"features\":[\"testing\"],\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"name\":\"opentelemetry_sdk\",\"req\":\"^0.32\"},{\"name\":\"prost\",\"optional\":true,\"req\":\"^0.14\"},{\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde_derive\",\"std\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"},{\"default_features\":false,\"features\":[\"codegen\"],\"name\":\"tonic\",\"optional\":true,\"req\":\"^0.14.1\"},{\"name\":\"tonic-prost\",\"optional\":true,\"req\":\"^0.14.1\"},{\"kind\":\"dev\",\"name\":\"tonic-prost-build\",\"req\":\"^0.14.1\"}],\"features\":{\"default\":[\"full\"],\"full\":[\"gen-tonic\",\"trace\",\"logs\",\"metrics\",\"zpages\",\"with-serde\",\"internal-logs\"],\"gen-tonic\":[\"gen-tonic-messages\",\"tonic\",\"tonic-prost\",\"tonic/channel\"],\"gen-tonic-messages\":[\"prost\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"logs\":[\"opentelemetry/logs\",\"opentelemetry_sdk/logs\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\"],\"profiles\":[],\"testing\":[\"opentelemetry/testing\"],\"trace\":[\"opentelemetry/trace\",\"opentelemetry_sdk/trace\"],\"with-schemars\":[\"schemars\"],\"with-serde\":[\"serde\",\"const-hex\",\"base64\"],\"zpages\":[\"trace\"]}}", + "opentelemetry-semantic-conventions_0.32.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"features\":[\"trace\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.32\"}],\"features\":{\"default\":[],\"semconv_experimental\":[]}}", + "opentelemetry_0.32.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-sink\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"js-sys\",\"req\":\"^0.3.63\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"os_rng\",\"thread_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"thiserror\",\"optional\":true,\"req\":\"^2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\">=0.1.40\"}],\"features\":{\"default\":[\"trace\",\"metrics\",\"logs\",\"internal-logs\",\"futures\"],\"experimental_metrics_bound_instruments\":[\"metrics\"],\"futures\":[\"futures-core\",\"futures-sink\",\"pin-project-lite\"],\"internal-logs\":[\"tracing\"],\"logs\":[],\"metrics\":[],\"testing\":[\"trace\"],\"trace\":[\"futures\",\"thiserror\"]}}", + "opentelemetry_sdk_0.32.1": "{\"dependencies\":[{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"futures-channel\",\"req\":\"^0.3\"},{\"name\":\"futures-executor\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\",\"sink\",\"async-await-macro\"],\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"optional\":true,\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.32\"},{\"default_features\":false,\"name\":\"opentelemetry-http\",\"optional\":true,\"req\":\"^0.32\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"fallback\"],\"name\":\"portable-atomic\",\"req\":\"^1\",\"target\":\"cfg(not(target_has_atomic = \\\"64\\\"))\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"std_rng\",\"small_rng\",\"os_rng\",\"thread_rng\"],\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.23.0\"},{\"default_features\":false,\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"url\",\"optional\":true,\"req\":\"^2.5\"}],\"features\":{\"bench_profiling\":[],\"default\":[\"trace\",\"metrics\",\"logs\",\"internal-logs\"],\"experimental_async_runtime\":[],\"experimental_logs_batch_log_processor_with_async_runtime\":[\"logs\",\"experimental_async_runtime\"],\"experimental_metrics_bound_instruments\":[\"metrics\",\"opentelemetry/experimental_metrics_bound_instruments\"],\"experimental_metrics_custom_reader\":[\"metrics\"],\"experimental_metrics_disable_name_validation\":[\"metrics\"],\"experimental_metrics_periodicreader_with_async_runtime\":[\"metrics\",\"experimental_async_runtime\"],\"experimental_trace_batch_span_processor_with_async_runtime\":[\"tokio/sync\",\"trace\",\"experimental_async_runtime\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"jaeger_remote_sampler\":[\"trace\",\"opentelemetry-http\",\"http\",\"serde\",\"serde_json\",\"url\",\"experimental_async_runtime\"],\"logs\":[\"opentelemetry/logs\"],\"metrics\":[\"opentelemetry/metrics\"],\"rt-tokio\":[\"tokio/rt\",\"tokio/time\",\"tokio-stream\",\"experimental_async_runtime\"],\"rt-tokio-current-thread\":[\"tokio/rt\",\"tokio/time\",\"tokio-stream\",\"experimental_async_runtime\"],\"spec_unstable_metrics_views\":[\"metrics\"],\"testing\":[\"opentelemetry/testing\",\"trace\",\"metrics\",\"logs\",\"tokio/sync\"],\"trace\":[\"opentelemetry/trace\",\"rand\",\"percent-encoding\"]}}", "option-ext_0.2.0": "{\"dependencies\":[],\"features\":{}}", "outref_0.5.2": "{\"dependencies\":[],\"features\":{}}", "p256_0.13.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"blobby\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"der\"],\"name\":\"ecdsa-core\",\"optional\":true,\"package\":\"ecdsa\",\"req\":\"^0.16\"},{\"default_features\":false,\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"ecdsa-core\",\"package\":\"ecdsa\",\"req\":\"^0.16\"},{\"default_features\":false,\"features\":[\"hazmat\",\"sec1\"],\"name\":\"elliptic-curve\",\"req\":\"^0.13.1\"},{\"name\":\"hex-literal\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4\"},{\"name\":\"primeorder\",\"optional\":true,\"req\":\"^0.13\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"primeorder\",\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"features\":[\"getrandom\"],\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"default_features\":false,\"name\":\"serdect\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"}],\"features\":{\"alloc\":[\"ecdsa-core?/alloc\",\"elliptic-curve/alloc\"],\"arithmetic\":[\"dep:primeorder\",\"elliptic-curve/arithmetic\"],\"bits\":[\"arithmetic\",\"elliptic-curve/bits\"],\"default\":[\"arithmetic\",\"ecdsa\",\"pem\",\"std\"],\"digest\":[\"ecdsa-core/digest\",\"ecdsa-core/hazmat\"],\"ecdh\":[\"arithmetic\",\"elliptic-curve/ecdh\"],\"ecdsa\":[\"arithmetic\",\"ecdsa-core/signing\",\"ecdsa-core/verifying\",\"sha256\"],\"expose-field\":[\"arithmetic\"],\"hash2curve\":[\"arithmetic\",\"elliptic-curve/hash2curve\"],\"jwk\":[\"elliptic-curve/jwk\"],\"pem\":[\"elliptic-curve/pem\",\"ecdsa-core/pem\",\"pkcs8\"],\"pkcs8\":[\"ecdsa-core?/pkcs8\",\"elliptic-curve/pkcs8\"],\"serde\":[\"ecdsa-core?/serde\",\"elliptic-curve/serde\",\"primeorder?/serde\",\"serdect\"],\"sha256\":[\"digest\",\"sha2\"],\"std\":[\"alloc\",\"ecdsa-core?/std\",\"elliptic-curve/std\"],\"test-vectors\":[\"dep:hex-literal\"],\"voprf\":[\"elliptic-curve/voprf\",\"sha2\"]}}", @@ -1118,7 +1118,6 @@ "pest_derive_2.8.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"pest\",\"req\":\"^2.8.3\"},{\"default_features\":false,\"name\":\"pest_generator\",\"req\":\"^2.8.3\"}],\"features\":{\"default\":[\"std\"],\"grammar-extras\":[\"pest_generator/grammar-extras\"],\"not-bootstrap-in-src\":[\"pest_generator/not-bootstrap-in-src\"],\"std\":[\"pest/std\",\"pest_generator/std\"]}}", "pest_generator_2.8.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"pest\",\"req\":\"^2.8.3\"},{\"name\":\"pest_meta\",\"req\":\"^2.8.3\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"default\":[\"std\"],\"export-internal\":[],\"grammar-extras\":[\"pest_meta/grammar-extras\"],\"not-bootstrap-in-src\":[\"pest_meta/not-bootstrap-in-src\"],\"std\":[\"pest/std\"]}}", "pest_meta_2.8.3": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cargo\",\"optional\":true,\"req\":\"^0.81.0\"},{\"name\":\"pest\",\"req\":\"^2.8.3\"},{\"default_features\":false,\"kind\":\"build\",\"name\":\"sha2\",\"req\":\"^0.10\"}],\"features\":{\"default\":[],\"grammar-extras\":[],\"not-bootstrap-in-src\":[\"dep:cargo\"]}}", - "petgraph_0.7.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.7.2\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"kind\":\"dev\",\"name\":\"defmac\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"fixedbitset\",\"req\":\"^0.5.7\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"name\":\"indexmap\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"odds\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.5.5\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.5.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"all\":[\"unstable\",\"quickcheck\",\"matrix_graph\",\"stable_graph\",\"graphmap\",\"rayon\"],\"default\":[\"graphmap\",\"stable_graph\",\"matrix_graph\"],\"generate\":[],\"graphmap\":[],\"matrix_graph\":[],\"rayon\":[\"dep:rayon\",\"indexmap/rayon\"],\"serde-1\":[\"serde\",\"serde_derive\"],\"stable_graph\":[],\"unstable\":[\"generate\"]}}", "petgraph_0.8.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.7.2\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"kind\":\"dev\",\"name\":\"defmac\",\"req\":\"^0.2.1\"},{\"name\":\"dot-parser\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"dot-parser-macros\",\"optional\":true,\"req\":\"^0.5.1\"},{\"default_features\":false,\"name\":\"fixedbitset\",\"req\":\"^0.5.7\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"default-hasher\",\"inline-more\"],\"name\":\"hashbrown\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"indexmap\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"odds\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.5.5\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.5.3\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"all\":[\"unstable\",\"quickcheck\",\"matrix_graph\",\"stable_graph\",\"graphmap\",\"rayon\",\"dot_parser\"],\"default\":[\"std\",\"graphmap\",\"stable_graph\",\"matrix_graph\"],\"dot_parser\":[\"std\",\"dep:dot-parser\",\"dep:dot-parser-macros\"],\"generate\":[],\"graphmap\":[],\"matrix_graph\":[],\"quickcheck\":[\"std\",\"dep:quickcheck\",\"graphmap\",\"stable_graph\"],\"rayon\":[\"std\",\"dep:rayon\",\"indexmap/rayon\",\"hashbrown/rayon\"],\"serde-1\":[\"serde\",\"serde_derive\"],\"stable_graph\":[\"serde?/alloc\"],\"std\":[\"indexmap/std\"],\"unstable\":[\"generate\"]}}", "pin-project-internal_1.1.10": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1.0.25\"},{\"default_features\":false,\"features\":[\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\",\"full\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0.1\"}],\"features\":{}}", "pin-project-internal_1.1.11": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1.0.25\"},{\"default_features\":false,\"features\":[\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\",\"full\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0.1\"}],\"features\":{}}", @@ -1140,14 +1139,14 @@ "prettyplease_0.2.37": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"indoc\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"default_features\":false,\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.105\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"extra-traits\",\"parsing\",\"printing\",\"visit-mut\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.105\"}],\"features\":{\"verbatim\":[\"syn/parsing\"]}}", "primeorder_0.13.6": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"arithmetic\",\"sec1\"],\"name\":\"elliptic-curve\",\"req\":\"^0.13.7\"},{\"default_features\":false,\"name\":\"serdect\",\"optional\":true,\"req\":\"^0.2\"}],\"features\":{\"alloc\":[\"elliptic-curve/alloc\"],\"dev\":[],\"serde\":[\"elliptic-curve/serde\",\"serdect\"],\"std\":[\"alloc\",\"elliptic-curve/std\"]}}", "proc-macro2_1.0.106": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4\"},{\"name\":\"unicode-ident\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"proc-macro\"],\"nightly\":[],\"proc-macro\":[],\"span-locations\":[]}}", - "prost-build_0.13.5": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"heck\",\"req\":\">=0.4, <=0.5\"},{\"default_features\":false,\"features\":[\"use_alloc\"],\"name\":\"itertools\",\"req\":\">=0.10, <=0.14\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"default_features\":false,\"name\":\"multimap\",\"req\":\">=0.8, <=0.10\"},{\"name\":\"once_cell\",\"req\":\"^1.17.1\"},{\"default_features\":false,\"name\":\"petgraph\",\"req\":\">=0.6, <=0.7\"},{\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"prost\",\"req\":\"^0.13.5\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.13.5\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"pulldown-cmark-to-cmark\",\"optional\":true,\"req\":\">=16, <=20\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-bool\"],\"name\":\"regex\",\"req\":\"^1.8.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"cleanup-markdown\":[\"dep:pulldown-cmark\",\"dep:pulldown-cmark-to-cmark\"],\"default\":[\"format\"],\"format\":[\"dep:prettyplease\",\"dep:syn\"]}}", "prost-build_0.14.3": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"heck\",\"req\":\">=0.4, <=0.5\"},{\"default_features\":false,\"features\":[\"use_alloc\"],\"name\":\"itertools\",\"req\":\">=0.10, <=0.14\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"default_features\":false,\"name\":\"multimap\",\"req\":\">=0.8, <=0.10\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"petgraph\",\"req\":\"^0.8\"},{\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"prost\",\"req\":\"^0.14.3\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.14.3\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"pulldown-cmark-to-cmark\",\"optional\":true,\"req\":\"^22\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-bool\"],\"name\":\"regex\",\"req\":\"^1.8.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"cleanup-markdown\":[\"dep:pulldown-cmark\",\"dep:pulldown-cmark-to-cmark\"],\"default\":[\"format\"],\"format\":[\"dep:prettyplease\",\"dep:syn\"]}}", - "prost-derive_0.13.5": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.1\"},{\"name\":\"itertools\",\"req\":\">=0.10.1, <=0.14\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", + "prost-build_0.14.4": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"heck\",\"req\":\">=0.4, <=0.5\"},{\"default_features\":false,\"features\":[\"use_alloc\"],\"name\":\"itertools\",\"req\":\">=0.10, <=0.14\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"default_features\":false,\"name\":\"multimap\",\"req\":\">=0.8, <=0.10\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"petgraph\",\"req\":\"^0.8\"},{\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"prost\",\"req\":\"^0.14.4\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.14.4\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"pulldown-cmark-to-cmark\",\"optional\":true,\"req\":\"^22\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-bool\"],\"name\":\"regex\",\"req\":\"^1.8.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"},{\"default_features\":false,\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"cleanup-markdown\":[\"dep:pulldown-cmark\",\"dep:pulldown-cmark-to-cmark\"],\"default\":[\"format\"],\"format\":[\"dep:prettyplease\",\"dep:syn\"]}}", "prost-derive_0.14.3": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.1\"},{\"name\":\"itertools\",\"req\":\">=0.10.1, <=0.14\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", - "prost-types_0.13.5": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"prost-derive\"],\"name\":\"prost\",\"req\":\"^0.13.5\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"std\"],\"std\":[\"prost/std\"]}}", + "prost-derive_0.14.4": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.1\"},{\"name\":\"itertools\",\"req\":\">=0.10.1, <=0.14\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "prost-types_0.14.3": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.4\"},{\"default_features\":false,\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.34\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"prost\",\"req\":\"^0.14.3\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"std\"],\"std\":[\"prost/std\"]}}", - "prost_0.13.5": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"prost-derive\",\"optional\":true,\"req\":\"^0.13.5\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"default\":[\"derive\",\"std\"],\"derive\":[\"dep:prost-derive\"],\"no-recursion-limit\":[],\"prost-derive\":[\"derive\"],\"std\":[]}}", + "prost-types_0.14.4": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.4\"},{\"default_features\":false,\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.34\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"prost\",\"req\":\"^0.14.4\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"std\"],\"std\":[\"prost/std\"]}}", "prost_0.14.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"prost-derive\",\"optional\":true,\"req\":\"^0.14.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"default\":[\"derive\",\"std\"],\"derive\":[\"dep:prost-derive\"],\"no-recursion-limit\":[],\"std\":[]}}", + "prost_0.14.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"prost-derive\",\"optional\":true,\"req\":\"^0.14.4\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10\"}],\"features\":{\"default\":[\"derive\",\"std\"],\"derive\":[\"dep:prost-derive\"],\"no-recursion-limit\":[],\"std\":[]}}", "protoc-gen-prost_0.5.0": "{\"dependencies\":[{\"name\":\"once_cell\",\"req\":\"^1.21.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"prost\",\"req\":\"^0.14.1\"},{\"default_features\":false,\"name\":\"prost-build\",\"req\":\"^0.14.1\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.14.1\"},{\"default_features\":false,\"name\":\"regex\",\"req\":\"^1.11.1\"}],\"features\":{}}", "protoc-gen-tonic_0.5.0": "{\"dependencies\":[{\"name\":\"heck\",\"req\":\"^0.5.0\"},{\"name\":\"prettyplease\",\"req\":\"^0.2.37\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.103\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"prost\",\"req\":\"^0.14.1\"},{\"default_features\":false,\"name\":\"prost-build\",\"req\":\"^0.14.1\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.14.1\"},{\"name\":\"protoc-gen-prost\",\"req\":\"^0.5.0\"},{\"name\":\"quote\",\"req\":\"^1.0.42\"},{\"default_features\":false,\"name\":\"regex\",\"req\":\"^1.11.1\"},{\"features\":[\"parsing\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.109\"},{\"name\":\"tonic-build\",\"req\":\"^0.14.1\"}],\"features\":{}}", "pyo3-build-config_0.28.3": "{\"dependencies\":[{\"name\":\"python3-dll-a\",\"optional\":true,\"req\":\"^0.2.12\"},{\"kind\":\"build\",\"name\":\"python3-dll-a\",\"optional\":true,\"req\":\"^0.2.12\"},{\"name\":\"target-lexicon\",\"req\":\"^0.13.3\"},{\"kind\":\"build\",\"name\":\"target-lexicon\",\"req\":\"^0.13.3\"}],\"features\":{\"abi3\":[],\"abi3-py310\":[\"abi3-py311\"],\"abi3-py311\":[\"abi3-py312\"],\"abi3-py312\":[\"abi3-py313\"],\"abi3-py313\":[\"abi3-py314\"],\"abi3-py314\":[\"abi3\"],\"abi3-py37\":[\"abi3-py38\"],\"abi3-py38\":[\"abi3-py39\"],\"abi3-py39\":[\"abi3-py310\"],\"default\":[],\"extension-module\":[],\"generate-import-lib\":[\"dep:python3-dll-a\"],\"resolve-config\":[]}}", @@ -1301,11 +1300,14 @@ "tokio-util_0.7.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3.0\"},{\"name\":\"bytes\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"futures-sink\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.5\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.0\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.44.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\"}],\"features\":{\"__docs_rs\":[\"futures-util\"],\"codec\":[],\"compat\":[\"futures-io\"],\"default\":[],\"full\":[\"codec\",\"compat\",\"io-util\",\"time\",\"net\",\"rt\",\"join-map\"],\"io\":[],\"io-util\":[\"io\",\"tokio/rt\",\"tokio/io-util\"],\"join-map\":[\"rt\",\"hashbrown\"],\"net\":[\"tokio/net\"],\"rt\":[\"tokio/rt\",\"tokio/sync\",\"futures-util\"],\"time\":[\"tokio/time\",\"slab\"]}}", "tokio_1.50.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.58\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.2.1\"},{\"features\":[\"async-await\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-concurrency\",\"req\":\"^7.6.3\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"io-uring\",\"optional\":true,\"req\":\"^0.7.11\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0.1\"},{\"default_features\":false,\"features\":[\"os-poll\",\"os-ext\"],\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0.1\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"mio-aio\",\"req\":\"^1\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"mockall\",\"req\":\"^0.13.0\"},{\"default_features\":false,\"features\":[\"aio\",\"fs\",\"socket\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.29.0\",\"target\":\"cfg(unix)\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"signal-hook-registry\",\"optional\":true,\"req\":\"^1.1.1\",\"target\":\"cfg(unix)\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.9\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"socket2\",\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"tokio-macros\",\"optional\":true,\"req\":\"~2.6.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"features\":[\"rt\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\",\"target\":\"cfg(tokio_unstable)\"},{\"kind\":\"dev\",\"name\":\"tracing-mock\",\"req\":\"=0.1.0-beta.1\",\"target\":\"cfg(all(tokio_unstable, target_has_atomic = \\\"64\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.0\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.61\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Authorization\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"fs\":[],\"full\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"process\",\"rt\",\"rt-multi-thread\",\"signal\",\"sync\",\"time\"],\"io-std\":[],\"io-uring\":[\"dep:io-uring\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"dep:slab\"],\"io-util\":[\"bytes\"],\"macros\":[\"tokio-macros\"],\"net\":[\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"socket2\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_Security\",\"windows-sys/Win32_Storage_FileSystem\",\"windows-sys/Win32_System_Pipes\",\"windows-sys/Win32_System_SystemServices\"],\"process\":[\"bytes\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Threading\",\"windows-sys/Win32_System_WindowsProgramming\"],\"rt\":[],\"rt-multi-thread\":[\"rt\"],\"signal\":[\"libc\",\"mio/os-poll\",\"mio/net\",\"mio/os-ext\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Console\"],\"sync\":[],\"taskdump\":[\"dep:backtrace\"],\"test-util\":[\"rt\",\"sync\",\"time\"],\"time\":[]}}", "tokio_1.52.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.58\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"kind\":\"dev\",\"name\":\"backtrace\",\"req\":\"^0.3.58\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.2.1\"},{\"features\":[\"async-await\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-concurrency\",\"req\":\"^7.6.3\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"io-uring\",\"optional\":true,\"req\":\"^0.7.11\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"mio\",\"optional\":true,\"req\":\"^1.2.0\"},{\"default_features\":false,\"features\":[\"os-poll\",\"os-ext\"],\"name\":\"mio\",\"optional\":true,\"req\":\"^1.2.0\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"mio-aio\",\"req\":\"^2\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"mockall\",\"req\":\"^0.13.0\"},{\"default_features\":false,\"features\":[\"aio\",\"fs\",\"socket\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.31.0\",\"target\":\"cfg(unix)\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"signal-hook-registry\",\"optional\":true,\"req\":\"^1.1.1\",\"target\":\"cfg(unix)\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.9\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6.3\",\"target\":\"cfg(any(not(target_family = \\\"wasm\\\"), all(target_os = \\\"wasi\\\", not(target_env = \\\"p1\\\"))))\"},{\"kind\":\"dev\",\"name\":\"socket2\",\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"tokio-macros\",\"optional\":true,\"req\":\"~2.7.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"features\":[\"rt\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\",\"target\":\"cfg(tokio_unstable)\"},{\"kind\":\"dev\",\"name\":\"tracing-mock\",\"req\":\"=0.1.0-beta.1\",\"target\":\"cfg(all(tokio_unstable, target_has_atomic = \\\"64\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.0\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.61\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Authorization\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"fs\":[],\"full\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"process\",\"rt\",\"rt-multi-thread\",\"signal\",\"sync\",\"time\"],\"io-std\":[],\"io-uring\":[\"dep:io-uring\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"dep:slab\"],\"io-util\":[\"bytes\"],\"macros\":[\"tokio-macros\"],\"net\":[\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"socket2\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_Security\",\"windows-sys/Win32_Storage_FileSystem\",\"windows-sys/Win32_System_Pipes\",\"windows-sys/Win32_System_SystemServices\"],\"process\":[\"bytes\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Threading\",\"windows-sys/Win32_System_WindowsProgramming\"],\"rt\":[],\"rt-multi-thread\":[\"rt\"],\"signal\":[\"libc\",\"mio/os-poll\",\"mio/net\",\"mio/os-ext\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Console\"],\"sync\":[],\"taskdump\":[\"dep:backtrace\"],\"test-util\":[\"rt\",\"sync\",\"time\"],\"time\":[]}}", - "tonic-build_0.13.1": "{\"dependencies\":[{\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"prost-build\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"prost-types\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"cleanup-markdown\":[\"prost-build?/cleanup-markdown\"],\"default\":[\"transport\",\"prost\"],\"prost\":[\"prost-build\",\"dep:prost-types\"],\"transport\":[]}}", "tonic-build_0.14.5": "{\"dependencies\":[{\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"default\":[\"transport\"],\"transport\":[]}}", + "tonic-build_0.14.6": "{\"dependencies\":[{\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"default\":[\"transport\"],\"transport\":[]}}", + "tonic-prost-build_0.14.6": "{\"dependencies\":[{\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"prost-build\",\"req\":\"^0.14\"},{\"name\":\"prost-types\",\"req\":\"^0.14\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"tempfile\",\"req\":\"^3.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"tonic\",\"req\":\"^0.14.6\"},{\"default_features\":false,\"name\":\"tonic-build\",\"req\":\"^0.14.6\"}],\"features\":{\"cleanup-markdown\":[\"prost-build/cleanup-markdown\"],\"default\":[\"transport\",\"cleanup-markdown\"],\"transport\":[\"tonic-build/transport\"]}}", "tonic-prost_0.14.5": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"prost\",\"req\":\"^0.14\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"req\":\"^0.14.0\"}],\"features\":{}}", - "tonic_0.13.1": "{\"dependencies\":[{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.13\"},{\"default_features\":false,\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"http1\",\"http2\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"hyper-timeout\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project\",\"req\":\"^1.0.11\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"prost\",\"optional\":true,\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"logging\",\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26.1\"},{\"default_features\":false,\"name\":\"tokio-stream\",\"req\":\"^0.1.16\"},{\"default_features\":false,\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^0.26\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13.0\"}],\"features\":{\"_tls-any\":[\"dep:tokio-rustls\",\"dep:tokio\",\"tokio?/rt\",\"tokio?/macros\"],\"channel\":[\"dep:hyper\",\"hyper?/client\",\"dep:hyper-util\",\"hyper-util?/client-legacy\",\"dep:tower\",\"tower?/balance\",\"tower?/buffer\",\"tower?/discover\",\"tower?/limit\",\"tower?/util\",\"dep:tokio\",\"tokio?/time\",\"dep:hyper-timeout\"],\"codegen\":[\"dep:async-trait\"],\"default\":[\"router\",\"transport\",\"codegen\",\"prost\"],\"deflate\":[\"dep:flate2\"],\"gzip\":[\"dep:flate2\"],\"prost\":[\"dep:prost\"],\"router\":[\"dep:axum\",\"dep:tower\",\"tower?/util\"],\"server\":[\"dep:h2\",\"dep:hyper\",\"hyper?/server\",\"dep:hyper-util\",\"hyper-util?/service\",\"hyper-util?/server-auto\",\"dep:socket2\",\"dep:tokio\",\"tokio?/macros\",\"tokio?/net\",\"tokio?/time\",\"tokio-stream/net\",\"dep:tower\",\"tower?/util\",\"tower?/limit\"],\"tls-aws-lc\":[\"_tls-any\",\"tokio-rustls/aws-lc-rs\"],\"tls-native-roots\":[\"_tls-any\",\"channel\",\"dep:rustls-native-certs\"],\"tls-ring\":[\"_tls-any\",\"tokio-rustls/ring\"],\"tls-webpki-roots\":[\"_tls-any\",\"channel\",\"dep:webpki-roots\"],\"transport\":[\"server\",\"channel\"],\"zstd\":[\"dep:zstd\"]}}", + "tonic-prost_0.14.6": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"prost\",\"req\":\"^0.14\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"req\":\"^0.14.6\"}],\"features\":{}}", + "tonic-types_0.14.6": "{\"dependencies\":[{\"name\":\"prost\",\"req\":\"^0.14\"},{\"name\":\"prost-types\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"tonic\",\"req\":\"^0.14.6\"}],\"features\":{}}", "tonic_0.14.5": "{\"dependencies\":[{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.13\"},{\"default_features\":false,\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"http\",\"req\":\"^1.1.0\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"http1\",\"http2\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"hyper-timeout\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.11\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project\",\"req\":\"^1.0.11\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.0\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"logging\",\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26.1\"},{\"default_features\":false,\"name\":\"tokio-stream\",\"req\":\"^0.1.16\"},{\"default_features\":false,\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"load-shed\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13.0\"}],\"features\":{\"_tls-any\":[\"dep:tokio\",\"tokio?/rt\",\"tokio?/macros\",\"tls-connect-info\"],\"channel\":[\"dep:hyper\",\"hyper?/client\",\"dep:hyper-util\",\"hyper-util?/client-legacy\",\"dep:tower\",\"tower?/balance\",\"tower?/buffer\",\"tower?/discover\",\"tower?/limit\",\"tower?/load-shed\",\"tower?/util\",\"dep:tokio\",\"tokio?/time\",\"dep:hyper-timeout\"],\"codegen\":[\"dep:async-trait\"],\"default\":[\"router\",\"transport\",\"codegen\"],\"deflate\":[\"dep:flate2\"],\"gzip\":[\"dep:flate2\"],\"router\":[\"dep:axum\",\"dep:tower\",\"tower?/util\"],\"server\":[\"dep:h2\",\"dep:hyper\",\"hyper?/server\",\"dep:hyper-util\",\"hyper-util?/service\",\"hyper-util?/server-auto\",\"dep:socket2\",\"dep:tokio\",\"tokio?/macros\",\"tokio?/net\",\"tokio?/time\",\"tokio-stream/net\",\"dep:tower\",\"tower?/util\",\"tower?/limit\",\"tower?/load-shed\"],\"tls-aws-lc\":[\"_tls-any\",\"tokio-rustls/aws-lc-rs\"],\"tls-connect-info\":[\"dep:tokio-rustls\"],\"tls-native-roots\":[\"_tls-any\",\"channel\",\"dep:rustls-native-certs\"],\"tls-ring\":[\"_tls-any\",\"tokio-rustls/ring\"],\"tls-webpki-roots\":[\"_tls-any\",\"channel\",\"dep:webpki-roots\"],\"transport\":[\"server\",\"channel\"],\"zstd\":[\"dep:zstd\"]}}", + "tonic_0.14.6": "{\"dependencies\":[{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.13\"},{\"default_features\":false,\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"http\",\"req\":\"^1.1.0\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"http1\",\"http2\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"hyper-timeout\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.11\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project\",\"req\":\"^1.0.11\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.0\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"logging\",\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26.1\"},{\"default_features\":false,\"name\":\"tokio-stream\",\"req\":\"^0.1.16\"},{\"default_features\":false,\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"load-shed\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13.0\"}],\"features\":{\"_tls-any\":[\"dep:tokio\",\"tokio?/rt\",\"tokio?/macros\",\"tls-connect-info\"],\"channel\":[\"dep:hyper\",\"hyper?/client\",\"dep:hyper-util\",\"hyper-util?/client-legacy\",\"dep:tower\",\"tower?/balance\",\"tower?/buffer\",\"tower?/discover\",\"tower?/limit\",\"tower?/load-shed\",\"tower?/util\",\"dep:tokio\",\"tokio?/time\",\"dep:hyper-timeout\"],\"codegen\":[\"dep:async-trait\"],\"default\":[\"router\",\"transport\",\"codegen\"],\"deflate\":[\"dep:flate2\"],\"gzip\":[\"dep:flate2\"],\"router\":[\"dep:axum\",\"dep:tower\",\"tower?/util\"],\"server\":[\"dep:h2\",\"dep:hyper\",\"hyper?/server\",\"dep:hyper-util\",\"hyper-util?/service\",\"hyper-util?/server-auto\",\"dep:socket2\",\"dep:tokio\",\"tokio?/macros\",\"tokio?/net\",\"tokio?/time\",\"tokio-stream/net\",\"dep:tower\",\"tower?/util\",\"tower?/limit\",\"tower?/load-shed\"],\"tls-aws-lc\":[\"_tls-any\",\"tokio-rustls/aws-lc-rs\"],\"tls-connect-info\":[\"dep:tokio-rustls\"],\"tls-native-roots\":[\"_tls-any\",\"channel\",\"dep:rustls-native-certs\"],\"tls-ring\":[\"_tls-any\",\"tokio-rustls/ring\"],\"tls-webpki-roots\":[\"_tls-any\",\"channel\",\"dep:webpki-roots\"],\"transport\":[\"server\",\"channel\"],\"zstd\":[\"dep:zstd\"]}}", "tower-http_0.6.11": "{\"dependencies\":[{\"features\":[\"tokio\"],\"name\":\"async-compression\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bitflags\",\"req\":\"^2.0.2\"},{\"kind\":\"dev\",\"name\":\"brotli\",\"req\":\"^8\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.14\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.14\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"http-range-header\",\"optional\":true,\"req\":\"^0.4.0\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"client-legacy\",\"http1\",\"server\",\"service\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.17\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.1.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"sync_wrapper\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"buffer\",\"util\",\"retry\",\"make\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.5\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"zstd\",\"req\":\"^0.13\"}],\"features\":{\"add-extension\":[],\"async-compression\":[],\"auth\":[\"base64\",\"validate-request\"],\"catch-panic\":[\"tracing\",\"futures-util/std\",\"dep:http-body\",\"dep:http-body-util\"],\"compression-br\":[\"dep:async-compression\",\"async-compression?/brotli\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"dep:tokio\"],\"compression-deflate\":[\"dep:async-compression\",\"async-compression?/zlib\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"dep:tokio\"],\"compression-full\":[\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"compression-zstd\"],\"compression-gzip\":[\"dep:async-compression\",\"async-compression?/gzip\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"dep:tokio\"],\"compression-zstd\":[\"dep:async-compression\",\"async-compression?/zstd\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"dep:tokio\"],\"cors\":[],\"decompression-br\":[\"dep:async-compression\",\"async-compression?/brotli\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"dep:tokio\"],\"decompression-deflate\":[\"dep:async-compression\",\"async-compression?/zlib\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"dep:tokio\"],\"decompression-full\":[\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"decompression-zstd\"],\"decompression-gzip\":[\"dep:async-compression\",\"async-compression?/gzip\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"dep:tokio\"],\"decompression-zstd\":[\"dep:async-compression\",\"async-compression?/zstd\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"dep:tokio\"],\"default\":[],\"follow-redirect\":[\"futures-util\",\"dep:http-body\",\"dep:url\",\"tower/util\"],\"fs\":[\"dep:tokio\",\"tokio?/fs\",\"tokio?/io-util\",\"futures-core\",\"futures-util\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util/io\",\"dep:http-range-header\",\"mime_guess\",\"mime\",\"percent-encoding\",\"httpdate\",\"set-status\",\"futures-util/alloc\"],\"full\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-full\",\"cors\",\"decompression-full\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"on-early-drop\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"limit\":[\"dep:http-body\",\"dep:http-body-util\"],\"map-request-body\":[],\"map-response-body\":[],\"metrics\":[\"dep:http-body\",\"dep:tokio\",\"tokio?/time\"],\"normalize-path\":[],\"on-early-drop\":[\"dep:http-body\"],\"propagate-header\":[],\"redirect\":[],\"request-id\":[\"uuid\"],\"sensitive-headers\":[],\"set-header\":[],\"set-status\":[],\"timeout\":[\"dep:http-body\",\"dep:tokio\",\"tokio?/time\"],\"tokio\":[],\"trace\":[\"dep:http-body\",\"tracing\"],\"util\":[\"tower\"],\"validate-request\":[\"mime\"]}}", "tower-layer_0.3.3": "{\"dependencies\":[],\"features\":{}}", "tower-service_0.3.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.22\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^0.2\"},{\"features\":[\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.6.2\"},{\"kind\":\"dev\",\"name\":\"tower-layer\",\"req\":\"^0.3\"}],\"features\":{}}", @@ -1314,7 +1316,7 @@ "tracing-attributes_0.1.31": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1.67\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1.0.20\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"full\",\"parsing\",\"printing\",\"visit-mut\",\"clone-impls\",\"extra-traits\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"features\":[\"env-filter\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.64\"}],\"features\":{\"async-await\":[]}}", "tracing-core_0.1.36": "{\"dependencies\":[{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.13.0\"},{\"default_features\":false,\"name\":\"valuable\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"}],\"features\":{\"default\":[\"std\",\"valuable?/std\"],\"std\":[\"once_cell\"]}}", "tracing-log_0.2.0": "{\"dependencies\":[{\"name\":\"ahash\",\"optional\":true,\"req\":\"^0.7.6\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"name\":\"log\",\"req\":\"^0.4.17\"},{\"name\":\"lru\",\"optional\":true,\"req\":\"^0.7.7\"},{\"name\":\"once_cell\",\"req\":\"^1.13.0\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"}],\"features\":{\"default\":[\"log-tracer\",\"std\"],\"interest-cache\":[\"lru\",\"ahash\"],\"log-tracer\":[],\"std\":[\"log/std\"]}}", - "tracing-opentelemetry_0.31.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.56\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1.56\"},{\"default_features\":false,\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.17\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.17\"},{\"name\":\"js-sys\",\"req\":\"^0.3.64\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"lazy_static\",\"optional\":true,\"req\":\"^1.0.2\"},{\"name\":\"once_cell\",\"req\":\"^1.13.0\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.30.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.30.0\"},{\"features\":[\"metrics\",\"grpc-tonic\"],\"kind\":\"dev\",\"name\":\"opentelemetry-otlp\",\"req\":\"^0.30.0\"},{\"features\":[\"semconv_experimental\"],\"kind\":\"dev\",\"name\":\"opentelemetry-semantic-conventions\",\"req\":\"^0.30.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry-stdout\",\"req\":\"^0.30.0\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry_sdk\",\"req\":\"^0.30.0\"},{\"default_features\":false,\"features\":[\"trace\",\"rt-tokio\",\"experimental_metrics_custom_reader\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.30.0\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14.0\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"thiserror\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"thiserror-1\",\"optional\":true,\"package\":\"thiserror\",\"req\":\"^1.0.31\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"std\",\"attributes\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"},{\"kind\":\"dev\",\"name\":\"tracing-error\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"features\":[\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"name\":\"web-time\",\"req\":\"^1.0.0\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"}],\"features\":{\"default\":[\"tracing-log\",\"metrics\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\",\"smallvec\"],\"metrics_gauge_unstable\":[]}}", + "tracing-opentelemetry_0.33.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"js-sys\",\"req\":\"^0.3.64\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"lazy_static\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.32.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.32.0\"},{\"features\":[\"metrics\",\"grpc-tonic\"],\"kind\":\"dev\",\"name\":\"opentelemetry-otlp\",\"req\":\"^0.32.0\"},{\"features\":[\"semconv_experimental\"],\"kind\":\"dev\",\"name\":\"opentelemetry-semantic-conventions\",\"req\":\"^0.32.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry-stdout\",\"req\":\"^0.32.0\"},{\"default_features\":false,\"features\":[\"trace\",\"experimental_metrics_custom_reader\",\"testing\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.32.0\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.15.0\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"std\",\"attributes\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"},{\"kind\":\"dev\",\"name\":\"tracing-error\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.22\"},{\"default_features\":false,\"features\":[\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"name\":\"web-time\",\"req\":\"^1.0.0\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"}],\"features\":{\"default\":[\"tracing-log\",\"metrics\"],\"metrics\":[\"opentelemetry/metrics\",\"smallvec\"]}}", "tracing-serde_0.2.0": "{\"dependencies\":[{\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"},{\"default_features\":false,\"name\":\"valuable-serde\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"},{\"default_features\":false,\"name\":\"valuable_crate\",\"optional\":true,\"package\":\"valuable\",\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"}],\"features\":{\"valuable\":[\"valuable_crate\",\"valuable-serde\",\"tracing-core/valuable\"]}}", "tracing-subscriber_0.3.23": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"clock\",\"std\"],\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.26\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.17\"},{\"name\":\"matchers\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"nu-ansi-term\",\"optional\":true,\"req\":\"^0.50.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.13.0\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"regex-automata\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.140\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.82\"},{\"name\":\"sharded-slab\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.9.0\"},{\"name\":\"thread_local\",\"optional\":true,\"req\":\"^1.1.4\"},{\"features\":[\"formatting\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.2\"},{\"features\":[\"formatting\",\"macros\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.2\"},{\"features\":[\"rt\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.43\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.43\"},{\"default_features\":false,\"name\":\"tracing-core\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"std-future\",\"std\"],\"kind\":\"dev\",\"name\":\"tracing-futures\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"log-tracer\",\"std\"],\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"tracing-log\",\"req\":\"^0.2.0\"},{\"name\":\"tracing-serde\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"valuable-serde\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"},{\"default_features\":false,\"name\":\"valuable_crate\",\"optional\":true,\"package\":\"valuable\",\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"}],\"features\":{\"alloc\":[],\"ansi\":[\"fmt\",\"nu-ansi-term\"],\"default\":[\"smallvec\",\"fmt\",\"ansi\",\"tracing-log\",\"std\"],\"env-filter\":[\"matchers\",\"once_cell\",\"tracing\",\"std\",\"thread_local\",\"dep:regex-automata\"],\"fmt\":[\"registry\",\"std\"],\"json\":[\"tracing-serde\",\"serde\",\"serde_json\"],\"local-time\":[\"time/local-offset\"],\"nu-ansi-term\":[\"dep:nu-ansi-term\"],\"regex\":[],\"registry\":[\"sharded-slab\",\"thread_local\",\"std\"],\"std\":[\"alloc\",\"tracing-core/std\"],\"valuable\":[\"tracing-core/valuable\",\"valuable_crate\",\"valuable-serde\",\"tracing-serde/valuable\"]}}", "tracing-test-macro_0.2.5": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"no-env-filter\":[]}}", diff --git a/nativelink-error/BUILD.bazel b/nativelink-error/BUILD.bazel index e0d854b4c..59f4e1b25 100644 --- a/nativelink-error/BUILD.bazel +++ b/nativelink-error/BUILD.bazel @@ -16,6 +16,7 @@ rust_library( deps = [ "//nativelink-metric", "//nativelink-proto", + "@crates//:base64", "@crates//:mongodb", "@crates//:prost", "@crates//:prost-types", diff --git a/nativelink-error/Cargo.toml b/nativelink-error/Cargo.toml index 9c0cbde92..c0493106c 100644 --- a/nativelink-error/Cargo.toml +++ b/nativelink-error/Cargo.toml @@ -14,12 +14,13 @@ version = "1.5.2" nativelink-metric = { path = "../nativelink-metric" } nativelink-proto = { path = "../nativelink-proto" } +base64 = { version = "0.22.1", default-features = false, features = ["std"] } mongodb = { version = "3", features = [ "compat-3-0-0", "rustls-tls", ], default-features = false } -prost = { version = "0.13.5", default-features = false } -prost-types = { version = "0.13.5", default-features = false, features = [ +prost = { version = "0.14.4", default-features = false } +prost-types = { version = "0.14.4", default-features = false, features = [ "std", ] } redis = { version = "1.0.0", default-features = false } @@ -33,7 +34,7 @@ tokio = { version = "1.52.2", features = [ "rt-multi-thread", "signal", ], default-features = false } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "tls-ring", "transport", ], default-features = false } diff --git a/nativelink-error/src/lib.rs b/nativelink-error/src/lib.rs index 791e0bf26..8ecfef21c 100644 --- a/nativelink-error/src/lib.rs +++ b/nativelink-error/src/lib.rs @@ -418,6 +418,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: base64::DecodeError) -> Self { + Self::from_std_err(Code::Internal, &err) + } +} + pub trait ResultExt { /// # Errors /// diff --git a/nativelink-error/tests/lib_tests.rs b/nativelink-error/tests/lib_tests.rs index b1ee6200b..14ef666a1 100644 --- a/nativelink-error/tests/lib_tests.rs +++ b/nativelink-error/tests/lib_tests.rs @@ -275,7 +275,7 @@ fn test_error_make_input_err_macro() { #[test] fn test_error_conversion_from_prost_decode_error() { - let prost_error = prost::DecodeError::new("Decode failure"); + let prost_error = prost::DecodeError::new_unexpected_type_url("Decode failure", "http://foo"); let error: Error = prost_error.into(); assert_eq!(error.code, Code::Internal); assert!( diff --git a/nativelink-proto/BUILD.bazel b/nativelink-proto/BUILD.bazel index e621d0227..32a5e3c8d 100644 --- a/nativelink-proto/BUILD.bazel +++ b/nativelink-proto/BUILD.bazel @@ -33,8 +33,7 @@ rust_binary( srcs = ["gen_protos_tool.rs"], deps = [ "@crates//:clap", - "@crates//:prost-build", - "@crates//:tonic-build", + "@crates//:tonic-prost-build", ], ) @@ -155,6 +154,7 @@ rust_library( "@crates//:prost", "@crates//:prost-types", "@crates//:tonic", + "@crates//:tonic-prost", ], ) diff --git a/nativelink-proto/Cargo.toml b/nativelink-proto/Cargo.toml index 595e93870..599a896f2 100644 --- a/nativelink-proto/Cargo.toml +++ b/nativelink-proto/Cargo.toml @@ -13,21 +13,19 @@ path = "genproto/lib.rs" derive_more = { version = "2.0.1", default-features = false, features = [ "debug", ] } -prost = { version = "0.13.5", default-features = false } -prost-types = { version = "0.13.5", default-features = false } -tonic = { version = "0.13.0", features = [ +prost = { version = "0.14.4", default-features = false } +prost-types = { version = "0.14.4", default-features = false } +tonic = { version = "0.14.0", features = [ "codegen", - "prost", "tls-ring", "transport", ], default-features = false } +tonic-prost = { version = "0.14.6", default-features = false } [dev-dependencies] -prost-build = { version = "0.13.5", default-features = false } -tonic-build = { version = "0.13.0", features = [ - "prost", +tonic-prost-build = { version = "0.14.0", features = [ ], default-features = false } [package.metadata.cargo-machete] # Used by gen_protos_tool.rs -ignored = ["prost-build", "tonic-build"] +ignored = ["tonic-prost-build"] diff --git a/nativelink-proto/gen_protos_tool.rs b/nativelink-proto/gen_protos_tool.rs index 2374981e5..1a4520b3a 100644 --- a/nativelink-proto/gen_protos_tool.rs +++ b/nativelink-proto/gen_protos_tool.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::path::PathBuf; use clap::{Arg, ArgAction, Command}; -use prost_build::Config; +use tonic_prost_build::Config; fn main() -> std::io::Result<()> { let matches = Command::new("Rust gRPC Codegen") @@ -48,8 +48,8 @@ fn main() -> std::io::Result<()> { config.skip_debug(structs_with_data_to_ignore.keys()); - tonic_build::configure() + tonic_prost_build::configure() .out_dir(output_dir) - .compile_protos_with_config(config, &paths, &["nativelink-proto"])?; + .compile_with_config(config, &paths, &[&"nativelink-proto".into()])?; Ok(()) } diff --git a/nativelink-proto/genproto/blaze.invocation_policy.pb.rs b/nativelink-proto/genproto/blaze.invocation_policy.pb.rs index a10ac2e5c..aed41e8b4 100644 --- a/nativelink-proto/genproto/blaze.invocation_policy.pb.rs +++ b/nativelink-proto/genproto/blaze.invocation_policy.pb.rs @@ -27,7 +27,7 @@ pub struct InvocationPolicy { pub strategy_policy: ::core::option::Option, } /// A policy for controlling the value of a flag. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct FlagPolicy { /// The name of the flag to enforce this policy on. /// @@ -55,7 +55,7 @@ pub struct FlagPolicy { } /// Nested message and enum types in `FlagPolicy`. pub mod flag_policy { - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Operation { #[prost(message, tag = "3")] SetValue(super::SetValue), @@ -67,7 +67,7 @@ pub mod flag_policy { AllowValues(super::AllowValues), } } -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct SetValue { /// Use this value for the specified flag, overriding any default or user-set /// value (unless behavior = APPEND for repeatable flags). @@ -179,9 +179,9 @@ pub mod set_value { /// policy wins, later policies on this same flag will still remove the /// expanded UseDefault, so there is a way around, but it's really best not to /// use this on expansion flags at all. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct UseDefault {} -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DisallowValues { /// It is an error for the user to use any of these values (that is, the Bazel /// command will fail), unless new_value or use_default is set. @@ -201,7 +201,7 @@ pub struct DisallowValues { } /// Nested message and enum types in `DisallowValues`. pub mod disallow_values { - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum ReplacementValue { /// If set and if the value of the flag is disallowed (including the default /// value of the flag if the user doesn't specify a value), use this value as @@ -221,7 +221,7 @@ pub mod disallow_values { UseDefault(super::UseDefault), } } -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct AllowValues { /// It is an error for the user to use any value not in this list, unless /// new_value or use_default is set. @@ -232,7 +232,7 @@ pub struct AllowValues { } /// Nested message and enum types in `AllowValues`. pub mod allow_values { - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum ReplacementValue { /// If set and if the value of the flag is disallowed (including the default /// value of the flag if the user doesn't specify a value), use this value as diff --git a/nativelink-proto/genproto/blaze.pb.rs b/nativelink-proto/genproto/blaze.pb.rs index 109f397f8..711148724 100644 --- a/nativelink-proto/genproto/blaze.pb.rs +++ b/nativelink-proto/genproto/blaze.pb.rs @@ -42,7 +42,7 @@ pub struct ActionCacheStatistics { /// Nested message and enum types in `ActionCacheStatistics`. pub mod action_cache_statistics { /// Detailed information for a particular miss reason. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct MissDetail { #[prost(enumeration = "MissReason", tag = "1")] pub reason: i32, diff --git a/nativelink-proto/genproto/blaze.strategy_policy.pb.rs b/nativelink-proto/genproto/blaze.strategy_policy.pb.rs index f089b9349..a75c8db0c 100644 --- a/nativelink-proto/genproto/blaze.strategy_policy.pb.rs +++ b/nativelink-proto/genproto/blaze.strategy_policy.pb.rs @@ -56,7 +56,7 @@ pub struct MnemonicPolicy { pub strategy_allowlist: ::prost::alloc::vec::Vec, } /// Per-mnemonic allowlist settings. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct StrategiesForMnemonic { #[prost(string, optional, tag = "1")] pub mnemonic: ::core::option::Option<::prost::alloc::string::String>, diff --git a/nativelink-proto/genproto/build.bazel.remote.asset.v1.pb.rs b/nativelink-proto/genproto/build.bazel.remote.asset.v1.pb.rs index c2a863a12..631dda38d 100644 --- a/nativelink-proto/genproto/build.bazel.remote.asset.v1.pb.rs +++ b/nativelink-proto/genproto/build.bazel.remote.asset.v1.pb.rs @@ -27,7 +27,7 @@ /// qualifier is recommended for this purpose. /// /// Qualifiers may be supplied in any order. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Qualifier { /// The "name" of the qualifier, for example "resource_type". /// No separation is made between 'standard' and 'nonstandard' @@ -311,7 +311,7 @@ pub struct PushBlobRequest { } /// A response message for /// [Push.PushBlob][build.bazel.remote.asset.v1.Push.PushBlob]. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct PushBlobResponse {} /// A request message for /// [Push.PushDirectory][build.bazel.remote.asset.v1.Push.PushDirectory]. @@ -369,7 +369,7 @@ pub struct PushDirectoryRequest { } /// A response message for /// [Push.PushDirectory][build.bazel.remote.asset.v1.Push.PushDirectory]. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct PushDirectoryResponse {} /// Generated client implementations. pub mod fetch_client { @@ -531,7 +531,7 @@ pub mod fetch_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/build.bazel.remote.asset.v1.Fetch/FetchBlob", ); @@ -557,7 +557,7 @@ pub mod fetch_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/build.bazel.remote.asset.v1.Fetch/FetchDirectory", ); @@ -573,179 +573,6 @@ pub mod fetch_client { } } } -/// Generated client implementations. -pub mod push_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// The Push service is complementary to the Fetch, and allows for - /// associating contents of URLs to be returned in future Fetch API calls. - /// - /// As with other services in the Remote Execution API, any call may return an - /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing - /// information about when the client should retry the request; clients SHOULD - /// respect the information provided. - #[derive(Debug, Clone)] - pub struct PushClient { - inner: tonic::client::Grpc, - } - impl PushClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> PushClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - PushClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// These APIs associate the identifying information of a resource, as - /// indicated by URI and optionally Qualifiers, with content available in the - /// CAS. For example, associating a repository url and a commit id with a - /// Directory Digest. - /// - /// Servers *SHOULD* only allow trusted clients to associate content, and *MAY* - /// only allow certain URIs to be pushed. - /// - /// Clients *MUST* ensure associated content is available in CAS prior to - /// pushing. - /// - /// Clients *MUST* ensure the Qualifiers listed correctly match the contents, - /// and Servers *MAY* trust these values without validation. - /// Fetch servers *MAY* require exact match of all qualifiers when returning - /// content previously pushed, or allow fetching content with only a subset of - /// the qualifiers specified on Push. - /// - /// Clients can specify expiration information that the server *SHOULD* - /// respect. Subsequent requests can be used to alter the expiration time. - /// - /// A minimal compliant Fetch implementation may support only Push'd content - /// and return `NOT_FOUND` for any resource that was not pushed first. - /// Alternatively, a compliant implementation may choose to not support Push - /// and only return resources that can be Fetch'd from origin. - /// - /// Errors will be returned as gRPC Status errors. - /// The possible RPC errors include: - /// * `INVALID_ARGUMENT`: One or more arguments to the RPC were invalid. - /// * `RESOURCE_EXHAUSTED`: There is insufficient quota of some resource to - /// perform the requested operation. The client may retry after a delay. - /// * `UNAVAILABLE`: Due to a transient condition the operation could not be - /// completed. The client should retry. - /// * `INTERNAL`: An internal error occurred while performing the operation. - /// The client should retry. - pub async fn push_blob( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.asset.v1.Push/PushBlob", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("build.bazel.remote.asset.v1.Push", "PushBlob")); - self.inner.unary(req, path, codec).await - } - pub async fn push_directory( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.asset.v1.Push/PushDirectory", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new("build.bazel.remote.asset.v1.Push", "PushDirectory"), - ); - self.inner.unary(req, path, codec).await - } - } -} /// Generated server implementations. pub mod fetch_server { #![allow( @@ -943,7 +770,7 @@ pub mod fetch_server { let inner = self.inner.clone(); let fut = async move { let method = FetchBlobSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -988,7 +815,7 @@ pub mod fetch_server { let inner = self.inner.clone(); let fut = async move { let method = FetchDirectorySvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -1043,6 +870,179 @@ pub mod fetch_server { const NAME: &'static str = SERVICE_NAME; } } +/// Generated client implementations. +pub mod push_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// The Push service is complementary to the Fetch, and allows for + /// associating contents of URLs to be returned in future Fetch API calls. + /// + /// As with other services in the Remote Execution API, any call may return an + /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing + /// information about when the client should retry the request; clients SHOULD + /// respect the information provided. + #[derive(Debug, Clone)] + pub struct PushClient { + inner: tonic::client::Grpc, + } + impl PushClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> PushClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + PushClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// These APIs associate the identifying information of a resource, as + /// indicated by URI and optionally Qualifiers, with content available in the + /// CAS. For example, associating a repository url and a commit id with a + /// Directory Digest. + /// + /// Servers *SHOULD* only allow trusted clients to associate content, and *MAY* + /// only allow certain URIs to be pushed. + /// + /// Clients *MUST* ensure associated content is available in CAS prior to + /// pushing. + /// + /// Clients *MUST* ensure the Qualifiers listed correctly match the contents, + /// and Servers *MAY* trust these values without validation. + /// Fetch servers *MAY* require exact match of all qualifiers when returning + /// content previously pushed, or allow fetching content with only a subset of + /// the qualifiers specified on Push. + /// + /// Clients can specify expiration information that the server *SHOULD* + /// respect. Subsequent requests can be used to alter the expiration time. + /// + /// A minimal compliant Fetch implementation may support only Push'd content + /// and return `NOT_FOUND` for any resource that was not pushed first. + /// Alternatively, a compliant implementation may choose to not support Push + /// and only return resources that can be Fetch'd from origin. + /// + /// Errors will be returned as gRPC Status errors. + /// The possible RPC errors include: + /// * `INVALID_ARGUMENT`: One or more arguments to the RPC were invalid. + /// * `RESOURCE_EXHAUSTED`: There is insufficient quota of some resource to + /// perform the requested operation. The client may retry after a delay. + /// * `UNAVAILABLE`: Due to a transient condition the operation could not be + /// completed. The client should retry. + /// * `INTERNAL`: An internal error occurred while performing the operation. + /// The client should retry. + pub async fn push_blob( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.asset.v1.Push/PushBlob", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("build.bazel.remote.asset.v1.Push", "PushBlob")); + self.inner.unary(req, path, codec).await + } + pub async fn push_directory( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.asset.v1.Push/PushDirectory", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("build.bazel.remote.asset.v1.Push", "PushDirectory"), + ); + self.inner.unary(req, path, codec).await + } + } +} /// Generated server implementations. pub mod push_server { #![allow( @@ -1216,7 +1216,7 @@ pub mod push_server { let inner = self.inner.clone(); let fut = async move { let method = PushBlobSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -1261,7 +1261,7 @@ pub mod push_server { let inner = self.inner.clone(); let fut = async move { let method = PushDirectorySvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, diff --git a/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs b/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs index 3ac4f4a25..b7c8f6634 100644 --- a/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs +++ b/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs @@ -260,7 +260,7 @@ pub struct Command { pub mod command { /// An `EnvironmentVariable` is one variable to set in the running program's /// environment. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct EnvironmentVariable { /// The variable name. #[prost(string, tag = "1")] @@ -308,7 +308,7 @@ pub mod platform { /// is implicitly part of the action digest, so even tiny changes in the names /// or values (like changing case) may result in different action cache /// entries. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Property { /// The property name. #[prost(string, tag = "1")] @@ -413,7 +413,7 @@ pub struct Directory { /// [SymlinkNodes][build.bazel.remote.execution.v2.SymlinkNode]. The server is /// responsible for specifying the property `name`s that it accepts. If /// permitted by the server, the same `name` may occur multiple times. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct NodeProperty { /// The property name. #[prost(string, tag = "1")] @@ -458,7 +458,7 @@ pub struct FileNode { /// A `DirectoryNode` represents a child of a /// [Directory][build.bazel.remote.execution.v2.Directory] which is itself /// a `Directory` and its associated metadata. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DirectoryNode { /// The name of the directory. #[prost(string, tag = "1")] @@ -521,7 +521,7 @@ pub struct SymlinkNode { /// Most protocol buffer implementations will always follow these rules when /// serializing, but care should be taken to avoid shortcuts. For instance, /// concatenating two messages to merge them may produce duplicate fields. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Digest { /// The hash. In the case of SHA-256, it will always be a lowercase hex string /// exactly 64 characters long. @@ -823,7 +823,7 @@ pub struct Tree { } /// An `OutputDirectory` is the output in an `ActionResult` corresponding to a /// directory's full contents rather than a single file. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct OutputDirectory { /// The full path of the directory relative to the working directory. The path /// separator is a forward slash `/`. Since this is a relative path, it MUST @@ -897,7 +897,7 @@ pub struct OutputSymlink { pub node_properties: ::core::option::Option, } /// An `ExecutionPolicy` can be used to control the scheduling of the action. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecutionPolicy { /// The priority (relative importance) of this action. Generally, a lower value /// means that the action should be run sooner than actions having a greater @@ -913,7 +913,7 @@ pub struct ExecutionPolicy { } /// A `ResultsCachePolicy` is used for fine-grained control over how action /// outputs are stored in the CAS and Action Cache. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ResultsCachePolicy { /// The priority (relative importance) of this content in the overall cache. /// Generally, a lower value means a longer retention time or other advantage, @@ -928,7 +928,7 @@ pub struct ResultsCachePolicy { } /// A request message for /// [Execution.Execute][build.bazel.remote.execution.v2.Execution.Execute]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecuteRequest { /// The instance of the execution system to operate against. A server may /// support multiple instances of the execution system (with their own workers, @@ -976,7 +976,7 @@ pub struct ExecuteRequest { pub digest_function: i32, } /// A `LogFile` is a log stored in the CAS. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct LogFile { /// The digest of the log contents. #[prost(message, optional, tag = "1")] @@ -1045,7 +1045,7 @@ pub struct ExecuteResponse { /// has reached the COMPLETED stage, it MUST set the [done /// field][google.longrunning.Operation.done] of the /// [Operation][google.longrunning.Operation] and terminate the stream. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecutionStage {} /// Nested message and enum types in `ExecutionStage`. pub mod execution_stage { @@ -1131,7 +1131,7 @@ pub struct ExecuteOperationMetadata { } /// A request message for /// [WaitExecution][build.bazel.remote.execution.v2.Execution.WaitExecution]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WaitExecutionRequest { /// The name of the [Operation][google.longrunning.Operation] /// returned by [Execute][build.bazel.remote.execution.v2.Execution.Execute]. @@ -1140,7 +1140,7 @@ pub struct WaitExecutionRequest { } /// A request message for /// [ActionCache.GetActionResult][build.bazel.remote.execution.v2.ActionCache.GetActionResult]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetActionResultRequest { /// The instance of the execution system to operate against. A server may /// support multiple instances of the execution system (with their own workers, @@ -1275,7 +1275,7 @@ pub struct BatchUpdateBlobsRequest { /// Nested message and enum types in `BatchUpdateBlobsRequest`. pub mod batch_update_blobs_request { /// A request corresponding to a single blob that the client wants to upload. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Request { /// The digest of the blob. This MUST be the digest of `data`. All /// digests MUST use the same digest function. @@ -1385,7 +1385,7 @@ pub mod batch_read_blobs_response { } /// A request message for /// [ContentAddressableStorage.GetTree][build.bazel.remote.execution.v2.ContentAddressableStorage.GetTree]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetTreeRequest { /// The instance of the execution system to operate against. A server may /// support multiple instances of the execution system (with their own workers, @@ -1439,7 +1439,7 @@ pub struct GetTreeResponse { } /// A request message for /// [Capabilities.GetCapabilities][build.bazel.remote.execution.v2.Capabilities.GetCapabilities]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetCapabilitiesRequest { /// The instance of the execution system to operate against. A server may /// support multiple instances of the execution system (with their own workers, @@ -1473,7 +1473,7 @@ pub struct ServerCapabilities { } /// The digest function used for converting values into keys for CAS and Action /// Cache. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct DigestFunction {} /// Nested message and enum types in `DigestFunction`. pub mod digest_function { @@ -1610,7 +1610,7 @@ pub mod digest_function { } } /// Describes the server/instance capabilities for updating the action cache. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionCacheUpdateCapabilities { #[prost(bool, tag = "1")] pub update_enabled: bool, @@ -1627,7 +1627,7 @@ pub struct PriorityCapabilities { /// Nested message and enum types in `PriorityCapabilities`. pub mod priority_capabilities { /// Supported range of priorities, including boundaries. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct PriorityRange { /// The minimum numeric value for this priority range, which represents the /// most urgent task or longest retained item. @@ -1640,7 +1640,7 @@ pub mod priority_capabilities { } } /// Describes how the server treats absolute symlink targets. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct SymlinkAbsolutePathStrategy {} /// Nested message and enum types in `SymlinkAbsolutePathStrategy`. pub mod symlink_absolute_path_strategy { @@ -1692,7 +1692,7 @@ pub mod symlink_absolute_path_strategy { } } /// Compression formats which may be supported. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Compressor {} /// Nested message and enum types in `Compressor`. pub mod compressor { @@ -1823,7 +1823,7 @@ pub struct ExecutionCapabilities { pub digest_functions: ::prost::alloc::vec::Vec, } /// Details for the tool used to call the API. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ToolDetails { /// Name of the tool, e.g. bazel. #[prost(string, tag = "1")] @@ -1845,7 +1845,7 @@ pub struct ToolDetails { /// Therefore, if the gRPC library is used to pass/retrieve this /// metadata, the user may ignore the base64 encoding and assume it is simply /// serialized as a binary message. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RequestMetadata { /// The details for the tool invoking the requests. #[prost(message, optional, tag = "1")] @@ -2058,7 +2058,7 @@ pub mod execution_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/build.bazel.remote.execution.v2.Execution/Execute", ); @@ -2105,7 +2105,7 @@ pub mod execution_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/build.bazel.remote.execution.v2.Execution/WaitExecution", ); @@ -2121,8 +2121,8 @@ pub mod execution_client { } } } -/// Generated client implementations. -pub mod action_cache_client { +/// Generated server implementations. +pub mod execution_server { #![allow( unused_variables, dead_code, @@ -2131,74 +2131,174 @@ pub mod action_cache_client { clippy::let_unit_value, )] use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// The action cache API is used to query whether a given action has already been - /// performed and, if so, retrieve its result. Unlike the - /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage], - /// which addresses blobs by their own content, the action cache addresses the - /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] by a - /// digest of the encoded [Action][build.bazel.remote.execution.v2.Action] - /// which produced them. - /// - /// The lifetime of entries in the action cache is implementation-specific, but - /// the server SHOULD assume that more recently used entries are more likely to - /// be used again. + /// Generated trait containing gRPC methods that should be implemented for use with ExecutionServer. + #[async_trait] + pub trait Execution: std::marker::Send + std::marker::Sync + 'static { + /// Server streaming response type for the Execute method. + type ExecuteStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::super::super::super::super::super::google::longrunning::Operation, + tonic::Status, + >, + > + + std::marker::Send + + 'static; + /// Execute an action remotely. + /// + /// In order to execute an action, the client must first upload all of the + /// inputs, the + /// [Command][build.bazel.remote.execution.v2.Command] to run, and the + /// [Action][build.bazel.remote.execution.v2.Action] into the + /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage]. + /// It then calls `Execute` with an `action_digest` referring to them. The + /// server will run the action and eventually return the result. + /// + /// The input `Action`'s fields MUST meet the various canonicalization + /// requirements specified in the documentation for their types so that it has + /// the same digest as other logically equivalent `Action`s. The server MAY + /// enforce the requirements and return errors if a non-canonical input is + /// received. It MAY also proceed without verifying some or all of the + /// requirements, such as for performance reasons. If the server does not + /// verify the requirement, then it will treat the `Action` as distinct from + /// another logically equivalent action if they hash differently. + /// + /// Returns a stream of + /// [google.longrunning.Operation][google.longrunning.Operation] messages + /// describing the resulting execution, with eventual `response` + /// [ExecuteResponse][build.bazel.remote.execution.v2.ExecuteResponse]. The + /// `metadata` on the operation is of type + /// [ExecuteOperationMetadata][build.bazel.remote.execution.v2.ExecuteOperationMetadata]. + /// + /// If the client remains connected after the first response is returned after + /// the server, then updates are streamed as if the client had called + /// [WaitExecution][build.bazel.remote.execution.v2.Execution.WaitExecution] + /// until the execution completes or the request reaches an error. The + /// operation can also be queried using [Operations + /// API][google.longrunning.Operations.GetOperation]. + /// + /// The server NEED NOT implement other methods or functionality of the + /// Operations API. + /// + /// Errors discovered during creation of the `Operation` will be reported + /// as gRPC Status errors, while errors that occurred while running the + /// action will be reported in the `status` field of the `ExecuteResponse`. The + /// server MUST NOT set the `error` field of the `Operation` proto. + /// The possible errors include: + /// + /// * `INVALID_ARGUMENT`: One or more arguments are invalid. + /// * `FAILED_PRECONDITION`: One or more errors occurred in setting up the + /// action requested, such as a missing input or command or no worker being + /// available. The client may be able to fix the errors and retry. + /// * `RESOURCE_EXHAUSTED`: There is insufficient quota of some resource to run + /// the action. + /// * `UNAVAILABLE`: Due to a transient condition, such as all workers being + /// occupied (and the server does not support a queue), the action could not + /// be started. The client should retry. + /// * `INTERNAL`: An internal error occurred in the execution engine or the + /// worker. + /// * `DEADLINE_EXCEEDED`: The execution timed out. + /// * `CANCELLED`: The operation was cancelled by the client. This status is + /// only possible if the server implements the Operations API CancelOperation + /// method, and it was called for the current execution. + /// + /// In the case of a missing input or command, the server SHOULD additionally + /// send a [PreconditionFailure][google.rpc.PreconditionFailure] error detail + /// where, for each requested blob not present in the CAS, there is a + /// `Violation` with a `type` of `MISSING` and a `subject` of + /// `"blobs/{digest_function/}{hash}/{size}"` indicating the digest of the + /// missing blob. The `subject` is formatted the same way as the + /// `resource_name` provided to + /// [ByteStream.Read][google.bytestream.ByteStream.Read], with the leading + /// instance name omitted. `digest_function` MUST thus be omitted if its value + /// is one of MD5, MURMUR3, SHA1, SHA256, SHA384, SHA512, or VSO. + /// + /// The server does not need to guarantee that a call to this method leads to + /// at most one execution of the action. The server MAY execute the action + /// multiple times, potentially in parallel. These redundant executions MAY + /// continue to run, even if the operation is completed. + async fn execute( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the WaitExecution method. + type WaitExecutionStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::super::super::super::super::super::google::longrunning::Operation, + tonic::Status, + >, + > + + std::marker::Send + + 'static; + /// Wait for an execution operation to complete. When the client initially + /// makes the request, the server immediately responds with the current status + /// of the execution. The server will leave the request stream open until the + /// operation completes, and then respond with the completed operation. The + /// server MAY choose to stream additional updates as execution progresses, + /// such as to provide an update as to the state of the execution. + /// + /// In addition to the cases describe for Execute, the WaitExecution method + /// may fail as follows: + /// + /// * `NOT_FOUND`: The operation no longer exists due to any of a transient + /// condition, an unknown operation name, or if the server implements the + /// Operations API DeleteOperation method and it was called for the current + /// execution. The client should call `Execute` to retry. + async fn wait_execution( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// The Remote Execution API is used to execute an + /// [Action][build.bazel.remote.execution.v2.Action] on the remote + /// workers. /// /// As with other services in the Remote Execution API, any call may return an /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing /// information about when the client should retry the request; clients SHOULD /// respect the information provided. - #[derive(Debug, Clone)] - pub struct ActionCacheClient { - inner: tonic::client::Grpc, + #[derive(Debug)] + pub struct ExecutionServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, } - impl ActionCacheClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { + impl ExecutionServer { pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } + Self::from_arc(Arc::new(inner)) } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } } pub fn with_interceptor( inner: T, interceptor: F, - ) -> ActionCacheClient> + ) -> InterceptedService where F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, { - ActionCacheClient::new(InterceptedService::new(inner, interceptor)) + InterceptedService::new(Self::new(inner), interceptor) } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. + /// Enable decompressing requests with the given encoding. #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); self } - /// Enable decompressing responses. + /// Compress responses with the given encoding, if the client supports it. #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); self } /// Limits the maximum size of a decoded message. @@ -2206,7 +2306,7 @@ pub mod action_cache_client { /// Default: `4MB` #[must_use] pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); + self.max_decoding_message_size = Some(limit); self } /// Limits the maximum size of an encoded message. @@ -2214,514 +2314,161 @@ pub mod action_cache_client { /// Default: `usize::MAX` #[must_use] pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); + self.max_encoding_message_size = Some(limit); self } - /// Retrieve a cached execution result. - /// - /// Implementations SHOULD ensure that any blobs referenced from the - /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage] - /// are available at the time of returning the - /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] and will be - /// for some period of time afterwards. The lifetimes of the referenced blobs SHOULD be increased - /// if necessary and applicable. - /// - /// Errors: - /// - /// * `NOT_FOUND`: The requested `ActionResult` is not in the cache. - pub async fn get_action_result( + } + impl tonic::codegen::Service> for ExecutionServer + where + T: Execution, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.ActionCache/GetActionResult", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "build.bazel.remote.execution.v2.ActionCache", - "GetActionResult", - ), - ); - self.inner.unary(req, path, codec).await - } - /// Upload a new execution result. - /// - /// In order to allow the server to perform access control based on the type of - /// action, and to assist with client debugging, the client MUST first upload - /// the [Action][build.bazel.remote.execution.v2.Execution] that produced the - /// result, along with its - /// [Command][build.bazel.remote.execution.v2.Command], into the - /// `ContentAddressableStorage`. - /// - /// Server implementations MAY modify the - /// `UpdateActionResultRequest.action_result` and return an equivalent value. - /// - /// Errors: - /// - /// * `INVALID_ARGUMENT`: One or more arguments are invalid. - /// * `FAILED_PRECONDITION`: One or more errors occurred in updating the - /// action result, such as a missing command or action. - /// * `RESOURCE_EXHAUSTED`: There is insufficient storage space to add the - /// entry to the cache. - pub async fn update_action_result( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.ActionCache/UpdateActionResult", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "build.bazel.remote.execution.v2.ActionCache", - "UpdateActionResult", - ), - ); - self.inner.unary(req, path, codec).await - } - } -} -/// Generated client implementations. -pub mod content_addressable_storage_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - /// The CAS (content-addressable storage) is used to store the inputs to and - /// outputs from the execution service. Each piece of content is addressed by the - /// digest of its binary data. - /// - /// Most of the binary data stored in the CAS is opaque to the execution engine, - /// and is only used as a communication medium. In order to build an - /// [Action][build.bazel.remote.execution.v2.Action], - /// however, the client will need to also upload the - /// [Command][build.bazel.remote.execution.v2.Command] and input root - /// [Directory][build.bazel.remote.execution.v2.Directory] for the Action. - /// The Command and Directory messages must be marshalled to wire format and then - /// uploaded under the hash as with any other piece of content. In practice, the - /// input root directory is likely to refer to other Directories in its - /// hierarchy, which must also each be uploaded on their own. - /// - /// For small file uploads the client should group them together and call - /// [BatchUpdateBlobs][build.bazel.remote.execution.v2.ContentAddressableStorage.BatchUpdateBlobs]. - /// - /// For large uploads, the client must use the - /// [Write method][google.bytestream.ByteStream.Write] of the ByteStream API. - /// - /// For uncompressed data, The `WriteRequest.resource_name` is of the following form: - /// `{instance_name}/uploads/{uuid}/blobs/{digest_function/}{hash}/{size}{/optional_metadata}` - /// - /// Where: - /// * `instance_name` is an identifier used to distinguish between the various - /// instances on the server. Syntax and semantics of this field are defined - /// by the server; Clients must not make any assumptions about it (e.g., - /// whether it spans multiple path segments or not). If it is the empty path, - /// the leading slash is omitted, so that the `resource_name` becomes - /// `uploads/{uuid}/blobs/{digest_function/}{hash}/{size}{/optional_metadata}`. - /// To simplify parsing, a path segment cannot equal any of the following - /// keywords: `blobs`, `uploads`, `actions`, `actionResults`, `operations`, - /// `capabilities` or `compressed-blobs`. - /// * `uuid` is a version 4 UUID generated by the client, used to avoid - /// collisions between concurrent uploads of the same data. Clients MAY - /// reuse the same `uuid` for uploading different blobs. - /// * `digest_function` is a lowercase string form of a `DigestFunction.Value` - /// enum, indicating which digest function was used to compute `hash`. If the - /// digest function used is one of MD5, MURMUR3, SHA1, SHA256, SHA384, SHA512, - /// or VSO, this component MUST be omitted. In that case the server SHOULD - /// infer the digest function using the length of the `hash` and the digest - /// functions announced in the server's capabilities. - /// * `hash` and `size` refer to the [Digest][build.bazel.remote.execution.v2.Digest] - /// of the data being uploaded. - /// * `optional_metadata` is implementation specific data, which clients MAY omit. - /// Servers MAY ignore this metadata. - /// - /// Data can alternatively be uploaded in compressed form, with the following - /// `WriteRequest.resource_name` form: - /// `{instance_name}/uploads/{uuid}/compressed-blobs/{compressor}/{digest_function/}{uncompressed_hash}/{uncompressed_size}{/optional_metadata}` - /// - /// Where: - /// * `instance_name`, `uuid`, `digest_function` and `optional_metadata` are - /// defined as above. - /// * `compressor` is a lowercase string form of a `Compressor.Value` enum - /// other than `identity`, which is supported by the server and advertised in - /// [CacheCapabilities.supported_compressor][build.bazel.remote.execution.v2.CacheCapabilities.supported_compressor]. - /// * `uncompressed_hash` and `uncompressed_size` refer to the - /// [Digest][build.bazel.remote.execution.v2.Digest] of the data being - /// uploaded, once uncompressed. Servers MUST verify that these match - /// the uploaded data once uncompressed, and MUST return an - /// `INVALID_ARGUMENT` error in the case of mismatch. - /// - /// Note that when writing compressed blobs, the `WriteRequest.write_offset` in - /// the initial request in a stream refers to the offset in the uncompressed form - /// of the blob. In subsequent requests, `WriteRequest.write_offset` MUST be the - /// sum of the first request's 'WriteRequest.write_offset' and the total size of - /// all the compressed data bundles in the previous requests. - /// Note that this mixes an uncompressed offset with a compressed byte length, - /// which is nonsensical, but it is done to fit the semantics of the existing - /// ByteStream protocol. - /// - /// Uploads of the same data MAY occur concurrently in any form, compressed or - /// uncompressed. - /// - /// Clients SHOULD NOT use gRPC-level compression for ByteStream API `Write` - /// calls of compressed blobs, since this would compress already-compressed data. - /// - /// When attempting an upload, if another client has already completed the upload - /// (which may occur in the middle of a single upload if another client uploads - /// the same blob concurrently), the request will terminate immediately without - /// error, and with a response whose `committed_size` is the value `-1` if this - /// is a compressed upload, or with the full size of the uploaded file if this is - /// an uncompressed upload (regardless of how much data was transmitted by the - /// client). If the client completes the upload but the - /// [Digest][build.bazel.remote.execution.v2.Digest] does not match, an - /// `INVALID_ARGUMENT` error will be returned. In either case, the client should - /// not attempt to retry the upload. - /// - /// Small downloads can be grouped and requested in a batch via - /// [BatchReadBlobs][build.bazel.remote.execution.v2.ContentAddressableStorage.BatchReadBlobs]. - /// - /// For large downloads, the client must use the - /// [Read method][google.bytestream.ByteStream.Read] of the ByteStream API. - /// - /// For uncompressed data, The `ReadRequest.resource_name` is of the following form: - /// `{instance_name}/blobs/{digest_function/}{hash}/{size}` - /// Where `instance_name`, `digest_function`, `hash` and `size` are defined as - /// for uploads. - /// - /// Data can alternatively be downloaded in compressed form, with the following - /// `ReadRequest.resource_name` form: - /// `{instance_name}/compressed-blobs/{compressor}/{digest_function/}{uncompressed_hash}/{uncompressed_size}` - /// - /// Where: - /// * `instance_name`, `compressor` and `digest_function` are defined as for - /// uploads. - /// * `uncompressed_hash` and `uncompressed_size` refer to the - /// [Digest][build.bazel.remote.execution.v2.Digest] of the data being - /// downloaded, once uncompressed. Clients MUST verify that these match - /// the downloaded data once uncompressed, and take appropriate steps in - /// the case of failure such as retrying a limited number of times or - /// surfacing an error to the user. - /// - /// When downloading compressed blobs: - /// * `ReadRequest.read_offset` refers to the offset in the uncompressed form - /// of the blob. - /// * Servers MUST return `INVALID_ARGUMENT` if `ReadRequest.read_limit` is - /// non-zero. - /// * Servers MAY use any compression level they choose, including different - /// levels for different blobs (e.g. choosing a level designed for maximum - /// speed for data known to be incompressible). - /// * Clients SHOULD NOT use gRPC-level compression, since this would compress - /// already-compressed data. - /// - /// Servers MUST be able to provide data for all recently advertised blobs in - /// each of the compression formats that the server supports, as well as in - /// uncompressed form. - /// - /// The lifetime of entries in the CAS is implementation specific, but it SHOULD - /// be long enough to allow for newly-added and recently looked-up entries to be - /// used in subsequent calls (e.g. to - /// [Execute][build.bazel.remote.execution.v2.Execution.Execute]). - /// - /// Servers MUST behave as though empty blobs are always available, even if they - /// have not been uploaded. Clients MAY optimize away the uploading or - /// downloading of empty blobs. - /// - /// As with other services in the Remote Execution API, any call may return an - /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing - /// information about when the client should retry the request; clients SHOULD - /// respect the information provided. - #[derive(Debug, Clone)] - pub struct ContentAddressableStorageClient { - inner: tonic::client::Grpc, - } - impl ContentAddressableStorageClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> ContentAddressableStorageClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - ContentAddressableStorageClient::new( - InterceptedService::new(inner, interceptor), - ) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// Determine if blobs are present in the CAS. - /// - /// Clients can use this API before uploading blobs to determine which ones are - /// already present in the CAS and do not need to be uploaded again. - /// - /// Servers SHOULD increase the lifetimes of the referenced blobs if necessary and - /// applicable. - /// - /// There are no method-specific errors. - pub async fn find_missing_blobs( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.ContentAddressableStorage/FindMissingBlobs", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "build.bazel.remote.execution.v2.ContentAddressableStorage", - "FindMissingBlobs", - ), - ); - self.inner.unary(req, path, codec).await - } - /// Upload many blobs at once. - /// - /// The server may enforce a limit of the combined total size of blobs - /// to be uploaded using this API. This limit may be obtained using the - /// [Capabilities][build.bazel.remote.execution.v2.Capabilities] API. - /// Requests exceeding the limit should either be split into smaller - /// chunks or uploaded using the - /// [ByteStream API][google.bytestream.ByteStream], as appropriate. - /// - /// This request is equivalent to calling a Bytestream `Write` request - /// on each individual blob, in parallel. The requests may succeed or fail - /// independently. - /// - /// Errors: - /// - /// * `INVALID_ARGUMENT`: The client attempted to upload more than the - /// server supported limit. - /// - /// Individual requests may return the following errors, additionally: - /// - /// * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the blob. - /// * `INVALID_ARGUMENT`: The [Digest][build.bazel.remote.execution.v2.Digest] - /// does not match the provided data. - pub async fn batch_update_blobs( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.ContentAddressableStorage/BatchUpdateBlobs", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "build.bazel.remote.execution.v2.ContentAddressableStorage", - "BatchUpdateBlobs", - ), - ); - self.inner.unary(req, path, codec).await + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) } - /// Download many blobs at once. - /// - /// The server may enforce a limit of the combined total size of blobs - /// to be downloaded using this API. This limit may be obtained using the - /// [Capabilities][build.bazel.remote.execution.v2.Capabilities] API. - /// Requests exceeding the limit should either be split into smaller - /// chunks or downloaded using the - /// [ByteStream API][google.bytestream.ByteStream], as appropriate. - /// - /// This request is equivalent to calling a Bytestream `Read` request - /// on each individual blob, in parallel. The requests may succeed or fail - /// independently. - /// - /// Errors: - /// - /// * `INVALID_ARGUMENT`: The client attempted to read more than the - /// server supported limit. - /// - /// Every error on individual read will be returned in the corresponding digest - /// status. - pub async fn batch_read_blobs( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.ContentAddressableStorage/BatchReadBlobs", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "build.bazel.remote.execution.v2.ContentAddressableStorage", - "BatchReadBlobs", - ), - ); - self.inner.unary(req, path, codec).await + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/build.bazel.remote.execution.v2.Execution/Execute" => { + #[allow(non_camel_case_types)] + struct ExecuteSvc(pub Arc); + impl< + T: Execution, + > tonic::server::ServerStreamingService + for ExecuteSvc { + type Response = super::super::super::super::super::super::google::longrunning::Operation; + type ResponseStream = T::ExecuteStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::execute(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = ExecuteSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/build.bazel.remote.execution.v2.Execution/WaitExecution" => { + #[allow(non_camel_case_types)] + struct WaitExecutionSvc(pub Arc); + impl< + T: Execution, + > tonic::server::ServerStreamingService + for WaitExecutionSvc { + type Response = super::super::super::super::super::super::google::longrunning::Operation; + type ResponseStream = T::WaitExecutionStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::wait_execution(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = WaitExecutionSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new( + tonic::body::Body::default(), + ); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } } - /// Fetch the entire directory tree rooted at a node. - /// - /// This request must be targeted at a - /// [Directory][build.bazel.remote.execution.v2.Directory] stored in the - /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage] - /// (CAS). The server will enumerate the `Directory` tree recursively and - /// return every node descended from the root. - /// - /// The GetTreeRequest.page_token parameter can be used to skip ahead in - /// the stream (e.g. when retrying a partially completed and aborted request), - /// by setting it to a value taken from GetTreeResponse.next_page_token of the - /// last successfully processed GetTreeResponse). - /// - /// The exact traversal order is unspecified and, unless retrieving subsequent - /// pages from an earlier request, is not guaranteed to be stable across - /// multiple invocations of `GetTree`. - /// - /// If part of the tree is missing from the CAS, the server will return the - /// portion present and omit the rest. - /// - /// Errors: - /// - /// * `NOT_FOUND`: The requested tree root is not present in the CAS. - pub async fn get_tree( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.ContentAddressableStorage/GetTree", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "build.bazel.remote.execution.v2.ContentAddressableStorage", - "GetTree", - ), - ); - self.inner.server_streaming(req, path, codec).await + } + impl Clone for ExecutionServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } } } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "build.bazel.remote.execution.v2.Execution"; + impl tonic::server::NamedService for ExecutionServer { + const NAME: &'static str = SERVICE_NAME; + } } /// Generated client implementations. -pub mod capabilities_client { +pub mod action_cache_client { #![allow( unused_variables, dead_code, @@ -2731,17 +2478,27 @@ pub mod capabilities_client { )] use tonic::codegen::*; use tonic::codegen::http::Uri; - /// The Capabilities service may be used by remote execution clients to query - /// various server properties, in order to self-configure or return meaningful - /// error messages. + /// The action cache API is used to query whether a given action has already been + /// performed and, if so, retrieve its result. Unlike the + /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage], + /// which addresses blobs by their own content, the action cache addresses the + /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] by a + /// digest of the encoded [Action][build.bazel.remote.execution.v2.Action] + /// which produced them. /// - /// The query may include a particular `instance_name`, in which case the values - /// returned will pertain to that instance. + /// The lifetime of entries in the action cache is implementation-specific, but + /// the server SHOULD assume that more recently used entries are more likely to + /// be used again. + /// + /// As with other services in the Remote Execution API, any call may return an + /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing + /// information about when the client should retry the request; clients SHOULD + /// respect the information provided. #[derive(Debug, Clone)] - pub struct CapabilitiesClient { + pub struct ActionCacheClient { inner: tonic::client::Grpc, } - impl CapabilitiesClient + impl ActionCacheClient where T: tonic::client::GrpcService, T::Error: Into, @@ -2759,7 +2516,7 @@ pub mod capabilities_client { pub fn with_interceptor( inner: T, interceptor: F, - ) -> CapabilitiesClient> + ) -> ActionCacheClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, @@ -2773,7 +2530,7 @@ pub mod capabilities_client { http::Request, >>::Error: Into + std::marker::Send + std::marker::Sync, { - CapabilitiesClient::new(InterceptedService::new(inner, interceptor)) + ActionCacheClient::new(InterceptedService::new(inner, interceptor)) } /// Compress requests with the given encoding. /// @@ -2806,23 +2563,22 @@ pub mod capabilities_client { self.inner = self.inner.max_encoding_message_size(limit); self } - /// GetCapabilities returns the server capabilities configuration of the - /// remote endpoint. - /// Only the capabilities of the services supported by the endpoint will - /// be returned: - /// * Execution + CAS + Action Cache endpoints should return both - /// CacheCapabilities and ExecutionCapabilities. - /// * Execution only endpoints should return ExecutionCapabilities. - /// * CAS + Action Cache only endpoints should return CacheCapabilities. + /// Retrieve a cached execution result. /// - /// There are no method-specific errors. - pub async fn get_capabilities( + /// Implementations SHOULD ensure that any blobs referenced from the + /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage] + /// are available at the time of returning the + /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] and will be + /// for some period of time afterwards. The lifetimes of the referenced blobs SHOULD be increased + /// if necessary and applicable. + /// + /// Errors: + /// + /// * `NOT_FOUND`: The requested `ActionResult` is not in the cache. + pub async fn get_action_result( &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await @@ -2831,16 +2587,61 @@ pub mod capabilities_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/build.bazel.remote.execution.v2.Capabilities/GetCapabilities", + "/build.bazel.remote.execution.v2.ActionCache/GetActionResult", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( - "build.bazel.remote.execution.v2.Capabilities", - "GetCapabilities", + "build.bazel.remote.execution.v2.ActionCache", + "GetActionResult", + ), + ); + self.inner.unary(req, path, codec).await + } + /// Upload a new execution result. + /// + /// In order to allow the server to perform access control based on the type of + /// action, and to assist with client debugging, the client MUST first upload + /// the [Action][build.bazel.remote.execution.v2.Execution] that produced the + /// result, along with its + /// [Command][build.bazel.remote.execution.v2.Command], into the + /// `ContentAddressableStorage`. + /// + /// Server implementations MAY modify the + /// `UpdateActionResultRequest.action_result` and return an equivalent value. + /// + /// Errors: + /// + /// * `INVALID_ARGUMENT`: One or more arguments are invalid. + /// * `FAILED_PRECONDITION`: One or more errors occurred in updating the + /// action result, such as a missing command or action. + /// * `RESOURCE_EXHAUSTED`: There is insufficient storage space to add the + /// entry to the cache. + pub async fn update_action_result( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ActionCache/UpdateActionResult", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ActionCache", + "UpdateActionResult", ), ); self.inner.unary(req, path, codec).await @@ -2848,7 +2649,7 @@ pub mod capabilities_client { } } /// Generated server implementations. -pub mod execution_server { +pub mod action_cache_server { #![allow( unused_variables, dead_code, @@ -2857,143 +2658,74 @@ pub mod execution_server { clippy::let_unit_value, )] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with ExecutionServer. + /// Generated trait containing gRPC methods that should be implemented for use with ActionCacheServer. #[async_trait] - pub trait Execution: std::marker::Send + std::marker::Sync + 'static { - /// Server streaming response type for the Execute method. - type ExecuteStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::super::super::super::super::super::google::longrunning::Operation, - tonic::Status, - >, - > - + std::marker::Send - + 'static; - /// Execute an action remotely. + pub trait ActionCache: std::marker::Send + std::marker::Sync + 'static { + /// Retrieve a cached execution result. /// - /// In order to execute an action, the client must first upload all of the - /// inputs, the - /// [Command][build.bazel.remote.execution.v2.Command] to run, and the - /// [Action][build.bazel.remote.execution.v2.Action] into the - /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage]. - /// It then calls `Execute` with an `action_digest` referring to them. The - /// server will run the action and eventually return the result. + /// Implementations SHOULD ensure that any blobs referenced from the + /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage] + /// are available at the time of returning the + /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] and will be + /// for some period of time afterwards. The lifetimes of the referenced blobs SHOULD be increased + /// if necessary and applicable. /// - /// The input `Action`'s fields MUST meet the various canonicalization - /// requirements specified in the documentation for their types so that it has - /// the same digest as other logically equivalent `Action`s. The server MAY - /// enforce the requirements and return errors if a non-canonical input is - /// received. It MAY also proceed without verifying some or all of the - /// requirements, such as for performance reasons. If the server does not - /// verify the requirement, then it will treat the `Action` as distinct from - /// another logically equivalent action if they hash differently. + /// Errors: /// - /// Returns a stream of - /// [google.longrunning.Operation][google.longrunning.Operation] messages - /// describing the resulting execution, with eventual `response` - /// [ExecuteResponse][build.bazel.remote.execution.v2.ExecuteResponse]. The - /// `metadata` on the operation is of type - /// [ExecuteOperationMetadata][build.bazel.remote.execution.v2.ExecuteOperationMetadata]. + /// * `NOT_FOUND`: The requested `ActionResult` is not in the cache. + async fn get_action_result( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Upload a new execution result. /// - /// If the client remains connected after the first response is returned after - /// the server, then updates are streamed as if the client had called - /// [WaitExecution][build.bazel.remote.execution.v2.Execution.WaitExecution] - /// until the execution completes or the request reaches an error. The - /// operation can also be queried using [Operations - /// API][google.longrunning.Operations.GetOperation]. + /// In order to allow the server to perform access control based on the type of + /// action, and to assist with client debugging, the client MUST first upload + /// the [Action][build.bazel.remote.execution.v2.Execution] that produced the + /// result, along with its + /// [Command][build.bazel.remote.execution.v2.Command], into the + /// `ContentAddressableStorage`. /// - /// The server NEED NOT implement other methods or functionality of the - /// Operations API. + /// Server implementations MAY modify the + /// `UpdateActionResultRequest.action_result` and return an equivalent value. /// - /// Errors discovered during creation of the `Operation` will be reported - /// as gRPC Status errors, while errors that occurred while running the - /// action will be reported in the `status` field of the `ExecuteResponse`. The - /// server MUST NOT set the `error` field of the `Operation` proto. - /// The possible errors include: + /// Errors: /// /// * `INVALID_ARGUMENT`: One or more arguments are invalid. - /// * `FAILED_PRECONDITION`: One or more errors occurred in setting up the - /// action requested, such as a missing input or command or no worker being - /// available. The client may be able to fix the errors and retry. - /// * `RESOURCE_EXHAUSTED`: There is insufficient quota of some resource to run - /// the action. - /// * `UNAVAILABLE`: Due to a transient condition, such as all workers being - /// occupied (and the server does not support a queue), the action could not - /// be started. The client should retry. - /// * `INTERNAL`: An internal error occurred in the execution engine or the - /// worker. - /// * `DEADLINE_EXCEEDED`: The execution timed out. - /// * `CANCELLED`: The operation was cancelled by the client. This status is - /// only possible if the server implements the Operations API CancelOperation - /// method, and it was called for the current execution. - /// - /// In the case of a missing input or command, the server SHOULD additionally - /// send a [PreconditionFailure][google.rpc.PreconditionFailure] error detail - /// where, for each requested blob not present in the CAS, there is a - /// `Violation` with a `type` of `MISSING` and a `subject` of - /// `"blobs/{digest_function/}{hash}/{size}"` indicating the digest of the - /// missing blob. The `subject` is formatted the same way as the - /// `resource_name` provided to - /// [ByteStream.Read][google.bytestream.ByteStream.Read], with the leading - /// instance name omitted. `digest_function` MUST thus be omitted if its value - /// is one of MD5, MURMUR3, SHA1, SHA256, SHA384, SHA512, or VSO. - /// - /// The server does not need to guarantee that a call to this method leads to - /// at most one execution of the action. The server MAY execute the action - /// multiple times, potentially in parallel. These redundant executions MAY - /// continue to run, even if the operation is completed. - async fn execute( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - /// Server streaming response type for the WaitExecution method. - type WaitExecutionStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::super::super::super::super::super::google::longrunning::Operation, - tonic::Status, - >, - > - + std::marker::Send - + 'static; - /// Wait for an execution operation to complete. When the client initially - /// makes the request, the server immediately responds with the current status - /// of the execution. The server will leave the request stream open until the - /// operation completes, and then respond with the completed operation. The - /// server MAY choose to stream additional updates as execution progresses, - /// such as to provide an update as to the state of the execution. - /// - /// In addition to the cases describe for Execute, the WaitExecution method - /// may fail as follows: - /// - /// * `NOT_FOUND`: The operation no longer exists due to any of a transient - /// condition, an unknown operation name, or if the server implements the - /// Operations API DeleteOperation method and it was called for the current - /// execution. The client should call `Execute` to retry. - async fn wait_execution( + /// * `FAILED_PRECONDITION`: One or more errors occurred in updating the + /// action result, such as a missing command or action. + /// * `RESOURCE_EXHAUSTED`: There is insufficient storage space to add the + /// entry to the cache. + async fn update_action_result( &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; } - /// The Remote Execution API is used to execute an - /// [Action][build.bazel.remote.execution.v2.Action] on the remote - /// workers. + /// The action cache API is used to query whether a given action has already been + /// performed and, if so, retrieve its result. Unlike the + /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage], + /// which addresses blobs by their own content, the action cache addresses the + /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] by a + /// digest of the encoded [Action][build.bazel.remote.execution.v2.Action] + /// which produced them. + /// + /// The lifetime of entries in the action cache is implementation-specific, but + /// the server SHOULD assume that more recently used entries are more likely to + /// be used again. /// /// As with other services in the Remote Execution API, any call may return an /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing /// information about when the client should retry the request; clients SHOULD /// respect the information provided. #[derive(Debug)] - pub struct ExecutionServer { + pub struct ActionCacheServer { inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - impl ExecutionServer { + impl ActionCacheServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } @@ -3044,9 +2776,9 @@ pub mod execution_server { self } } - impl tonic::codegen::Service> for ExecutionServer + impl tonic::codegen::Service> for ActionCacheServer where - T: Execution, + T: ActionCache, B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { @@ -3061,26 +2793,25 @@ pub mod execution_server { } fn call(&mut self, req: http::Request) -> Self::Future { match req.uri().path() { - "/build.bazel.remote.execution.v2.Execution/Execute" => { + "/build.bazel.remote.execution.v2.ActionCache/GetActionResult" => { #[allow(non_camel_case_types)] - struct ExecuteSvc(pub Arc); + struct GetActionResultSvc(pub Arc); impl< - T: Execution, - > tonic::server::ServerStreamingService - for ExecuteSvc { - type Response = super::super::super::super::super::super::google::longrunning::Operation; - type ResponseStream = T::ExecuteStream; + T: ActionCache, + > tonic::server::UnaryService + for GetActionResultSvc { + type Response = super::ActionResult; type Future = BoxFuture< - tonic::Response, + tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::execute(&inner, request).await + ::get_action_result(&inner, request).await }; Box::pin(fut) } @@ -3091,8 +2822,8 @@ pub mod execution_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = ExecuteSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let method = GetActionResultSvc(inner); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -3102,31 +2833,31 @@ pub mod execution_server { max_decoding_message_size, max_encoding_message_size, ); - let res = grpc.server_streaming(method, req).await; + let res = grpc.unary(method, req).await; Ok(res) }; Box::pin(fut) } - "/build.bazel.remote.execution.v2.Execution/WaitExecution" => { + "/build.bazel.remote.execution.v2.ActionCache/UpdateActionResult" => { #[allow(non_camel_case_types)] - struct WaitExecutionSvc(pub Arc); + struct UpdateActionResultSvc(pub Arc); impl< - T: Execution, - > tonic::server::ServerStreamingService - for WaitExecutionSvc { - type Response = super::super::super::super::super::super::google::longrunning::Operation; - type ResponseStream = T::WaitExecutionStream; + T: ActionCache, + > tonic::server::UnaryService + for UpdateActionResultSvc { + type Response = super::ActionResult; type Future = BoxFuture< - tonic::Response, + tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::wait_execution(&inner, request).await + ::update_action_result(&inner, request) + .await }; Box::pin(fut) } @@ -3137,8 +2868,8 @@ pub mod execution_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = WaitExecutionSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let method = UpdateActionResultSvc(inner); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -3148,7 +2879,7 @@ pub mod execution_server { max_decoding_message_size, max_encoding_message_size, ); - let res = grpc.server_streaming(method, req).await; + let res = grpc.unary(method, req).await; Ok(res) }; Box::pin(fut) @@ -3175,7 +2906,7 @@ pub mod execution_server { } } } - impl Clone for ExecutionServer { + impl Clone for ActionCacheServer { fn clone(&self) -> Self { let inner = self.inner.clone(); Self { @@ -3188,13 +2919,13 @@ pub mod execution_server { } } /// Generated gRPC service name - pub const SERVICE_NAME: &str = "build.bazel.remote.execution.v2.Execution"; - impl tonic::server::NamedService for ExecutionServer { + pub const SERVICE_NAME: &str = "build.bazel.remote.execution.v2.ActionCache"; + impl tonic::server::NamedService for ActionCacheServer { const NAME: &'static str = SERVICE_NAME; } } -/// Generated server implementations. -pub mod action_cache_server { +/// Generated client implementations. +pub mod content_addressable_storage_client { #![allow( unused_variables, dead_code, @@ -3203,105 +2934,203 @@ pub mod action_cache_server { clippy::let_unit_value, )] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with ActionCacheServer. - #[async_trait] - pub trait ActionCache: std::marker::Send + std::marker::Sync + 'static { - /// Retrieve a cached execution result. - /// - /// Implementations SHOULD ensure that any blobs referenced from the - /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage] - /// are available at the time of returning the - /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] and will be - /// for some period of time afterwards. The lifetimes of the referenced blobs SHOULD be increased - /// if necessary and applicable. - /// - /// Errors: - /// - /// * `NOT_FOUND`: The requested `ActionResult` is not in the cache. - async fn get_action_result( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - /// Upload a new execution result. - /// - /// In order to allow the server to perform access control based on the type of - /// action, and to assist with client debugging, the client MUST first upload - /// the [Action][build.bazel.remote.execution.v2.Execution] that produced the - /// result, along with its - /// [Command][build.bazel.remote.execution.v2.Command], into the - /// `ContentAddressableStorage`. - /// - /// Server implementations MAY modify the - /// `UpdateActionResultRequest.action_result` and return an equivalent value. - /// - /// Errors: - /// - /// * `INVALID_ARGUMENT`: One or more arguments are invalid. - /// * `FAILED_PRECONDITION`: One or more errors occurred in updating the - /// action result, such as a missing command or action. - /// * `RESOURCE_EXHAUSTED`: There is insufficient storage space to add the - /// entry to the cache. - async fn update_action_result( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - } - /// The action cache API is used to query whether a given action has already been - /// performed and, if so, retrieve its result. Unlike the - /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage], - /// which addresses blobs by their own content, the action cache addresses the - /// [ActionResult][build.bazel.remote.execution.v2.ActionResult] by a - /// digest of the encoded [Action][build.bazel.remote.execution.v2.Action] - /// which produced them. + use tonic::codegen::http::Uri; + /// The CAS (content-addressable storage) is used to store the inputs to and + /// outputs from the execution service. Each piece of content is addressed by the + /// digest of its binary data. /// - /// The lifetime of entries in the action cache is implementation-specific, but - /// the server SHOULD assume that more recently used entries are more likely to - /// be used again. + /// Most of the binary data stored in the CAS is opaque to the execution engine, + /// and is only used as a communication medium. In order to build an + /// [Action][build.bazel.remote.execution.v2.Action], + /// however, the client will need to also upload the + /// [Command][build.bazel.remote.execution.v2.Command] and input root + /// [Directory][build.bazel.remote.execution.v2.Directory] for the Action. + /// The Command and Directory messages must be marshalled to wire format and then + /// uploaded under the hash as with any other piece of content. In practice, the + /// input root directory is likely to refer to other Directories in its + /// hierarchy, which must also each be uploaded on their own. + /// + /// For small file uploads the client should group them together and call + /// [BatchUpdateBlobs][build.bazel.remote.execution.v2.ContentAddressableStorage.BatchUpdateBlobs]. + /// + /// For large uploads, the client must use the + /// [Write method][google.bytestream.ByteStream.Write] of the ByteStream API. + /// + /// For uncompressed data, The `WriteRequest.resource_name` is of the following form: + /// `{instance_name}/uploads/{uuid}/blobs/{digest_function/}{hash}/{size}{/optional_metadata}` + /// + /// Where: + /// * `instance_name` is an identifier used to distinguish between the various + /// instances on the server. Syntax and semantics of this field are defined + /// by the server; Clients must not make any assumptions about it (e.g., + /// whether it spans multiple path segments or not). If it is the empty path, + /// the leading slash is omitted, so that the `resource_name` becomes + /// `uploads/{uuid}/blobs/{digest_function/}{hash}/{size}{/optional_metadata}`. + /// To simplify parsing, a path segment cannot equal any of the following + /// keywords: `blobs`, `uploads`, `actions`, `actionResults`, `operations`, + /// `capabilities` or `compressed-blobs`. + /// * `uuid` is a version 4 UUID generated by the client, used to avoid + /// collisions between concurrent uploads of the same data. Clients MAY + /// reuse the same `uuid` for uploading different blobs. + /// * `digest_function` is a lowercase string form of a `DigestFunction.Value` + /// enum, indicating which digest function was used to compute `hash`. If the + /// digest function used is one of MD5, MURMUR3, SHA1, SHA256, SHA384, SHA512, + /// or VSO, this component MUST be omitted. In that case the server SHOULD + /// infer the digest function using the length of the `hash` and the digest + /// functions announced in the server's capabilities. + /// * `hash` and `size` refer to the [Digest][build.bazel.remote.execution.v2.Digest] + /// of the data being uploaded. + /// * `optional_metadata` is implementation specific data, which clients MAY omit. + /// Servers MAY ignore this metadata. + /// + /// Data can alternatively be uploaded in compressed form, with the following + /// `WriteRequest.resource_name` form: + /// `{instance_name}/uploads/{uuid}/compressed-blobs/{compressor}/{digest_function/}{uncompressed_hash}/{uncompressed_size}{/optional_metadata}` + /// + /// Where: + /// * `instance_name`, `uuid`, `digest_function` and `optional_metadata` are + /// defined as above. + /// * `compressor` is a lowercase string form of a `Compressor.Value` enum + /// other than `identity`, which is supported by the server and advertised in + /// [CacheCapabilities.supported_compressor][build.bazel.remote.execution.v2.CacheCapabilities.supported_compressor]. + /// * `uncompressed_hash` and `uncompressed_size` refer to the + /// [Digest][build.bazel.remote.execution.v2.Digest] of the data being + /// uploaded, once uncompressed. Servers MUST verify that these match + /// the uploaded data once uncompressed, and MUST return an + /// `INVALID_ARGUMENT` error in the case of mismatch. + /// + /// Note that when writing compressed blobs, the `WriteRequest.write_offset` in + /// the initial request in a stream refers to the offset in the uncompressed form + /// of the blob. In subsequent requests, `WriteRequest.write_offset` MUST be the + /// sum of the first request's 'WriteRequest.write_offset' and the total size of + /// all the compressed data bundles in the previous requests. + /// Note that this mixes an uncompressed offset with a compressed byte length, + /// which is nonsensical, but it is done to fit the semantics of the existing + /// ByteStream protocol. + /// + /// Uploads of the same data MAY occur concurrently in any form, compressed or + /// uncompressed. + /// + /// Clients SHOULD NOT use gRPC-level compression for ByteStream API `Write` + /// calls of compressed blobs, since this would compress already-compressed data. + /// + /// When attempting an upload, if another client has already completed the upload + /// (which may occur in the middle of a single upload if another client uploads + /// the same blob concurrently), the request will terminate immediately without + /// error, and with a response whose `committed_size` is the value `-1` if this + /// is a compressed upload, or with the full size of the uploaded file if this is + /// an uncompressed upload (regardless of how much data was transmitted by the + /// client). If the client completes the upload but the + /// [Digest][build.bazel.remote.execution.v2.Digest] does not match, an + /// `INVALID_ARGUMENT` error will be returned. In either case, the client should + /// not attempt to retry the upload. + /// + /// Small downloads can be grouped and requested in a batch via + /// [BatchReadBlobs][build.bazel.remote.execution.v2.ContentAddressableStorage.BatchReadBlobs]. + /// + /// For large downloads, the client must use the + /// [Read method][google.bytestream.ByteStream.Read] of the ByteStream API. + /// + /// For uncompressed data, The `ReadRequest.resource_name` is of the following form: + /// `{instance_name}/blobs/{digest_function/}{hash}/{size}` + /// Where `instance_name`, `digest_function`, `hash` and `size` are defined as + /// for uploads. + /// + /// Data can alternatively be downloaded in compressed form, with the following + /// `ReadRequest.resource_name` form: + /// `{instance_name}/compressed-blobs/{compressor}/{digest_function/}{uncompressed_hash}/{uncompressed_size}` + /// + /// Where: + /// * `instance_name`, `compressor` and `digest_function` are defined as for + /// uploads. + /// * `uncompressed_hash` and `uncompressed_size` refer to the + /// [Digest][build.bazel.remote.execution.v2.Digest] of the data being + /// downloaded, once uncompressed. Clients MUST verify that these match + /// the downloaded data once uncompressed, and take appropriate steps in + /// the case of failure such as retrying a limited number of times or + /// surfacing an error to the user. + /// + /// When downloading compressed blobs: + /// * `ReadRequest.read_offset` refers to the offset in the uncompressed form + /// of the blob. + /// * Servers MUST return `INVALID_ARGUMENT` if `ReadRequest.read_limit` is + /// non-zero. + /// * Servers MAY use any compression level they choose, including different + /// levels for different blobs (e.g. choosing a level designed for maximum + /// speed for data known to be incompressible). + /// * Clients SHOULD NOT use gRPC-level compression, since this would compress + /// already-compressed data. + /// + /// Servers MUST be able to provide data for all recently advertised blobs in + /// each of the compression formats that the server supports, as well as in + /// uncompressed form. + /// + /// The lifetime of entries in the CAS is implementation specific, but it SHOULD + /// be long enough to allow for newly-added and recently looked-up entries to be + /// used in subsequent calls (e.g. to + /// [Execute][build.bazel.remote.execution.v2.Execution.Execute]). + /// + /// Servers MUST behave as though empty blobs are always available, even if they + /// have not been uploaded. Clients MAY optimize away the uploading or + /// downloading of empty blobs. /// /// As with other services in the Remote Execution API, any call may return an /// error with a [RetryInfo][google.rpc.RetryInfo] error detail providing - /// information about when the client should retry the request; clients SHOULD - /// respect the information provided. - #[derive(Debug)] - pub struct ActionCacheServer { - inner: Arc, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, + /// information about when the client should retry the request; clients SHOULD + /// respect the information provided. + #[derive(Debug, Clone)] + pub struct ContentAddressableStorageClient { + inner: tonic::client::Grpc, } - impl ActionCacheServer { + impl ContentAddressableStorageClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) + let inner = tonic::client::Grpc::new(inner); + Self { inner } } - pub fn from_arc(inner: Arc) -> Self { - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } } pub fn with_interceptor( inner: T, interceptor: F, - ) -> InterceptedService + ) -> ContentAddressableStorageClient> where F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, { - InterceptedService::new(Self::new(inner), interceptor) + ContentAddressableStorageClient::new( + InterceptedService::new(inner, interceptor), + ) } - /// Enable decompressing requests with the given encoding. + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); self } - /// Compress responses with the given encoding, if the client supports it. + /// Enable decompressing responses. #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); self } /// Limits the maximum size of a decoded message. @@ -3309,7 +3138,7 @@ pub mod action_cache_server { /// Default: `4MB` #[must_use] pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); + self.inner = self.inner.max_decoding_message_size(limit); self } /// Limits the maximum size of an encoded message. @@ -3317,156 +3146,200 @@ pub mod action_cache_server { /// Default: `usize::MAX` #[must_use] pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); + self.inner = self.inner.max_encoding_message_size(limit); self } - } - impl tonic::codegen::Service> for ActionCacheServer - where - T: ActionCache, - B: Body + std::marker::Send + 'static, - B::Error: Into + std::marker::Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( + /// Determine if blobs are present in the CAS. + /// + /// Clients can use this API before uploading blobs to determine which ones are + /// already present in the CAS and do not need to be uploaded again. + /// + /// Servers SHOULD increase the lifetimes of the referenced blobs if necessary and + /// applicable. + /// + /// There are no method-specific errors. + pub async fn find_missing_blobs( &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ContentAddressableStorage/FindMissingBlobs", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ContentAddressableStorage", + "FindMissingBlobs", + ), + ); + self.inner.unary(req, path, codec).await } - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/build.bazel.remote.execution.v2.ActionCache/GetActionResult" => { - #[allow(non_camel_case_types)] - struct GetActionResultSvc(pub Arc); - impl< - T: ActionCache, - > tonic::server::UnaryService - for GetActionResultSvc { - type Response = super::ActionResult; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_action_result(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = GetActionResultSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/build.bazel.remote.execution.v2.ActionCache/UpdateActionResult" => { - #[allow(non_camel_case_types)] - struct UpdateActionResultSvc(pub Arc); - impl< - T: ActionCache, - > tonic::server::UnaryService - for UpdateActionResultSvc { - type Response = super::ActionResult; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::update_action_result(&inner, request) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = UpdateActionResultSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - let mut response = http::Response::new( - tonic::body::Body::default(), - ); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } - } + /// Upload many blobs at once. + /// + /// The server may enforce a limit of the combined total size of blobs + /// to be uploaded using this API. This limit may be obtained using the + /// [Capabilities][build.bazel.remote.execution.v2.Capabilities] API. + /// Requests exceeding the limit should either be split into smaller + /// chunks or uploaded using the + /// [ByteStream API][google.bytestream.ByteStream], as appropriate. + /// + /// This request is equivalent to calling a Bytestream `Write` request + /// on each individual blob, in parallel. The requests may succeed or fail + /// independently. + /// + /// Errors: + /// + /// * `INVALID_ARGUMENT`: The client attempted to upload more than the + /// server supported limit. + /// + /// Individual requests may return the following errors, additionally: + /// + /// * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the blob. + /// * `INVALID_ARGUMENT`: The [Digest][build.bazel.remote.execution.v2.Digest] + /// does not match the provided data. + pub async fn batch_update_blobs( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ContentAddressableStorage/BatchUpdateBlobs", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ContentAddressableStorage", + "BatchUpdateBlobs", + ), + ); + self.inner.unary(req, path, codec).await } - } - impl Clone for ActionCacheServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } + /// Download many blobs at once. + /// + /// The server may enforce a limit of the combined total size of blobs + /// to be downloaded using this API. This limit may be obtained using the + /// [Capabilities][build.bazel.remote.execution.v2.Capabilities] API. + /// Requests exceeding the limit should either be split into smaller + /// chunks or downloaded using the + /// [ByteStream API][google.bytestream.ByteStream], as appropriate. + /// + /// This request is equivalent to calling a Bytestream `Read` request + /// on each individual blob, in parallel. The requests may succeed or fail + /// independently. + /// + /// Errors: + /// + /// * `INVALID_ARGUMENT`: The client attempted to read more than the + /// server supported limit. + /// + /// Every error on individual read will be returned in the corresponding digest + /// status. + pub async fn batch_read_blobs( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ContentAddressableStorage/BatchReadBlobs", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ContentAddressableStorage", + "BatchReadBlobs", + ), + ); + self.inner.unary(req, path, codec).await + } + /// Fetch the entire directory tree rooted at a node. + /// + /// This request must be targeted at a + /// [Directory][build.bazel.remote.execution.v2.Directory] stored in the + /// [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage] + /// (CAS). The server will enumerate the `Directory` tree recursively and + /// return every node descended from the root. + /// + /// The GetTreeRequest.page_token parameter can be used to skip ahead in + /// the stream (e.g. when retrying a partially completed and aborted request), + /// by setting it to a value taken from GetTreeResponse.next_page_token of the + /// last successfully processed GetTreeResponse). + /// + /// The exact traversal order is unspecified and, unless retrieving subsequent + /// pages from an earlier request, is not guaranteed to be stable across + /// multiple invocations of `GetTree`. + /// + /// If part of the tree is missing from the CAS, the server will return the + /// portion present and omit the rest. + /// + /// Errors: + /// + /// * `NOT_FOUND`: The requested tree root is not present in the CAS. + pub async fn get_tree( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ContentAddressableStorage/GetTree", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ContentAddressableStorage", + "GetTree", + ), + ); + self.inner.server_streaming(req, path, codec).await } - } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "build.bazel.remote.execution.v2.ActionCache"; - impl tonic::server::NamedService for ActionCacheServer { - const NAME: &'static str = SERVICE_NAME; } } /// Generated server implementations. @@ -3843,7 +3716,7 @@ pub mod content_addressable_storage_server { let inner = self.inner.clone(); let fut = async move { let method = FindMissingBlobsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -3892,7 +3765,7 @@ pub mod content_addressable_storage_server { let inner = self.inner.clone(); let fut = async move { let method = BatchUpdateBlobsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -3941,7 +3814,7 @@ pub mod content_addressable_storage_server { let inner = self.inner.clone(); let fut = async move { let method = BatchReadBlobsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -3988,7 +3861,7 @@ pub mod content_addressable_storage_server { let inner = self.inner.clone(); let fut = async move { let method = GetTreeSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -4043,6 +3916,133 @@ pub mod content_addressable_storage_server { const NAME: &'static str = SERVICE_NAME; } } +/// Generated client implementations. +pub mod capabilities_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// The Capabilities service may be used by remote execution clients to query + /// various server properties, in order to self-configure or return meaningful + /// error messages. + /// + /// The query may include a particular `instance_name`, in which case the values + /// returned will pertain to that instance. + #[derive(Debug, Clone)] + pub struct CapabilitiesClient { + inner: tonic::client::Grpc, + } + impl CapabilitiesClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> CapabilitiesClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + CapabilitiesClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// GetCapabilities returns the server capabilities configuration of the + /// remote endpoint. + /// Only the capabilities of the services supported by the endpoint will + /// be returned: + /// * Execution + CAS + Action Cache endpoints should return both + /// CacheCapabilities and ExecutionCapabilities. + /// * Execution only endpoints should return ExecutionCapabilities. + /// * CAS + Action Cache only endpoints should return CacheCapabilities. + /// + /// There are no method-specific errors. + pub async fn get_capabilities( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.Capabilities/GetCapabilities", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.Capabilities", + "GetCapabilities", + ), + ); + self.inner.unary(req, path, codec).await + } + } +} /// Generated server implementations. pub mod capabilities_server { #![allow( @@ -4186,7 +4186,7 @@ pub mod capabilities_server { let inner = self.inner.clone(); let fut = async move { let method = GetCapabilitiesSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, diff --git a/nativelink-proto/genproto/build.bazel.semver.pb.rs b/nativelink-proto/genproto/build.bazel.semver.pb.rs index 5cb0a9695..7cdd17fe8 100644 --- a/nativelink-proto/genproto/build.bazel.semver.pb.rs +++ b/nativelink-proto/genproto/build.bazel.semver.pb.rs @@ -14,7 +14,7 @@ // This file is @generated by prost-build. /// The full version of a given tool. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct SemVer { /// The major version, e.g 10 for 10.2.3. #[prost(int32, tag = "1")] diff --git a/nativelink-proto/genproto/build_event_stream.pb.rs b/nativelink-proto/genproto/build_event_stream.pb.rs index 4becc1cd5..279cf5da3 100644 --- a/nativelink-proto/genproto/build_event_stream.pb.rs +++ b/nativelink-proto/genproto/build_event_stream.pb.rs @@ -21,7 +21,7 @@ /// event has an id that is mentioned as child id in an earlier event and a build /// invocation is complete if and only if all direct and indirect children of the /// initial event have been posted. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildEventId { #[prost( oneof = "build_event_id::Id", @@ -34,14 +34,14 @@ pub mod build_event_id { /// Generic identifier for a build event. This is the default type of /// BuildEventId, but should not be used outside testing; nevertheless, /// tools should handle build events with this kind of id gracefully. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct UnknownBuildEventId { #[prost(string, tag = "1")] pub details: ::prost::alloc::string::String, } /// Identifier of an event reporting progress. Those events are also used to /// chain in events that come early. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ProgressId { /// Unique identifier. No assumption should be made about how the ids are /// assigned; the only meaningful operation on this field is test for @@ -51,14 +51,14 @@ pub mod build_event_id { } /// Identifier of an event indicating the beginning of a build; this will /// normally be the first event. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildStartedId {} /// Identifier on an event indicating the original commandline received by /// the bazel server. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct UnstructuredCommandLineId {} /// Identifier on an event describing the commandline received by Bazel. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct StructuredCommandLineId { /// A title for this command line value, as there may be multiple. /// For example, a single invocation may wish to report both the literal and @@ -68,15 +68,15 @@ pub mod build_event_id { pub command_line_label: ::prost::alloc::string::String, } /// Identifier of an event indicating the workspace status. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct WorkspaceStatusId {} /// Identifier on an event reporting on the options included in the command /// line, both explicitly and implicitly. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct OptionsParsedId {} /// Identifier of an event reporting that an external resource was fetched /// from. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct FetchId { /// The external resource that was fetched from. #[prost(string, tag = "1")] @@ -88,18 +88,18 @@ pub mod build_event_id { /// have been skipped for some reason, if the actual expansion was still /// carried out (e.g., if keep_going is set). In this case, the /// pattern_skipped choice in the id field is to be made. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PatternExpandedId { #[prost(string, repeated, tag = "1")] pub pattern: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct WorkspaceConfigId {} - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildMetadataId {} /// Identifier of an event indicating that a target has been expanded by /// identifying for which configurations it should be build. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetConfiguredId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -120,7 +120,7 @@ pub mod build_event_id { } /// Identifier of an event introducing a named set of files (usually artifacts) /// to be referred to in later messages. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct NamedSetOfFilesId { /// Identifier of the file set; this is an opaque string valid only for the /// particular instance of the event stream. @@ -128,7 +128,7 @@ pub mod build_event_id { pub id: ::prost::alloc::string::String, } /// Identifier of an event introducing a configuration. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConfigurationId { /// Identifier of the configuration; users of the protocol should not make /// any assumptions about it having any structure, or equality of the @@ -141,7 +141,7 @@ pub mod build_event_id { } /// Identifier of an event indicating that a target was built completely; this /// does not include running the test if the target is a test target. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetCompletedId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -166,7 +166,7 @@ pub mod build_event_id { /// Identifier of an event reporting that an action was completed (not all /// actions are reported, only the ones that can be considered important; /// this includes all failed actions). - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionCompletedId { #[prost(string, tag = "1")] pub primary_output: ::prost::alloc::string::String, @@ -182,7 +182,7 @@ pub mod build_event_id { /// any case, it will report some form of error (i.e., the payload will be an /// Aborted event); there are no regular events using this identifier. The /// purpose of those events is to serve as the root cause of a failed target. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct UnconfiguredLabelId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -191,7 +191,7 @@ pub mod build_event_id { /// label, usually a visibility error. In any case, an event with such an /// id will always report some form of error (i.e., the payload will be an /// Aborted event); there are no regular events using this identifier. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConfiguredLabelId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -203,7 +203,7 @@ pub mod build_event_id { /// in such a way as to uniquely identify the action within a build. In fact, /// attempts for the same test, run, shard triple are counted sequentially, /// starting with 1. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestResultId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -217,7 +217,7 @@ pub mod build_event_id { pub attempt: i32, } /// Identifier of an event reporting progress of an individual test run. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestProgressId { /// The label of the target for the action. #[prost(string, tag = "1")] @@ -241,7 +241,7 @@ pub mod build_event_id { pub opaque_count: i32, } /// Identifier of an event reporting the summary of a test. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestSummaryId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -249,7 +249,7 @@ pub mod build_event_id { pub configuration: ::core::option::Option, } /// Identifier of an event reporting the summary of a target. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetSummaryId { #[prost(string, tag = "1")] pub label: ::prost::alloc::string::String, @@ -257,23 +257,23 @@ pub mod build_event_id { pub configuration: ::core::option::Option, } /// Identifier of the BuildFinished event, indicating the end of a build. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildFinishedId {} /// Identifier of an event providing additional logs/statistics after /// completion of the build. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildToolLogsId {} /// Identifier of an event providing build metrics after completion /// of the build. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildMetricsId {} /// Identifier of an event providing convenience symlinks information. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConvenienceSymlinksIdentifiedId {} /// Identifier of an event providing the ExecRequest of a run command. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecRequestId {} - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Id { #[prost(message, tag = "1")] Unknown(UnknownBuildEventId), @@ -336,7 +336,7 @@ pub mod build_event_id { /// Payload of an event summarizing the progress of the build so far. Those /// events are also used to be parents of events where the more logical parent /// event cannot be posted yet as the needed information is not yet complete. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Progress { /// The next chunk of stdout that bazel produced since the last progress event /// or the beginning of the build. @@ -355,7 +355,7 @@ pub struct Progress { } /// Payload of an event indicating that an expected event will not come, as /// the build is aborted prematurely for some reason. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Aborted { #[prost(enumeration = "aborted::AbortReason", tag = "1")] pub reason: i32, @@ -452,7 +452,7 @@ pub mod aborted { /// to be build is contained in one of the announced child events; it is an /// invariant that precisely one of the announced child events has a non-empty /// target pattern. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildStarted { #[prost(string, tag = "1")] pub uuid: ::prost::alloc::string::String, @@ -487,7 +487,7 @@ pub struct BuildStarted { pub server_pid: i64, } /// Configuration related to the blaze workspace and output tree. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WorkspaceConfig { /// The root of the local blaze exec root. All output files live underneath /// this at "blaze-out/". @@ -500,7 +500,7 @@ pub struct WorkspaceConfig { /// like name and relevant entries of rc-files and client environment variables. /// However, it does contain enough information to reproduce the build /// invocation. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct UnstructuredCommandLine { #[prost(string, repeated, tag = "1")] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, @@ -528,7 +528,7 @@ pub struct OptionsParsed { /// Payload of an event indicating that an external resource was fetched. This /// event will only occur in streams where an actual fetch happened, not in ones /// where a cached copy of the entity to be fetched was used. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Fetch { #[prost(bool, tag = "1")] pub success: bool, @@ -544,7 +544,7 @@ pub struct WorkspaceStatus { } /// Nested message and enum types in `WorkspaceStatus`. pub mod workspace_status { - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Item { #[prost(string, tag = "1")] pub key: ::prost::alloc::string::String, @@ -599,7 +599,7 @@ pub mod pattern_expanded { /// Represents a test_suite target and the tests that it expanded to. Nested /// test suites are recursively expanded. The test labels only contain the /// final test targets, not any nested suites. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestSuiteExpansion { /// The label of the test_suite rule. #[prost(string, tag = "1")] @@ -615,7 +615,7 @@ pub mod pattern_expanded { /// been identified. As with pattern expansion the main information is in the /// chaining part: the id will contain the target that was configured and the /// children id will contain the configured targets it was configured to. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetConfigured { /// The kind of target (e.g., e.g. "cc_library rule", "source file", /// "generated file") where the completion is reported. @@ -629,7 +629,7 @@ pub struct TargetConfigured { #[prost(string, repeated, tag = "3")] pub tag: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct File { /// A sequence of prefixes to apply to the file name to construct a full path. /// In most but not all cases, there will be 3 entries: @@ -653,7 +653,7 @@ pub struct File { } /// Nested message and enum types in `File`. pub mod file { - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum File { /// A location where the contents of the file can be found. The string is /// encoded according to RFC2396. @@ -907,7 +907,7 @@ pub mod test_result { #[prost(message, optional, tag = "4")] pub time: ::core::option::Option<::prost_types::Duration>, } - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ResourceUsage { #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, @@ -917,7 +917,7 @@ pub mod test_result { } } /// Event payload providing information about an active, individual test run. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestProgress { /// Identifies a resource that may provide information about an active test /// run. The resource is not necessarily a file and may need to be queried @@ -993,7 +993,7 @@ pub struct TestSummary { pub total_run_duration: ::core::option::Option<::prost_types::Duration>, } /// Payload of the event summarizing a target (test or non-test). -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetSummary { /// Conjunction of TargetComplete events for this target, including aspects. #[prost(bool, tag = "1")] @@ -1039,7 +1039,7 @@ pub mod build_finished { /// rarely do) and are not part of the public API. /// /// A build was successful iff ExitCode.code equals 0. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExitCode { /// The name of the exit code. #[prost(string, tag = "1")] @@ -1049,7 +1049,7 @@ pub mod build_finished { pub code: i32, } /// Things that happened during the build that could be of interest. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct AnomalyReport { /// Was the build suspended at any time during the build. /// Examples of suspensions are SIGSTOP, or the hardware being put to sleep. @@ -1128,7 +1128,7 @@ pub mod build_metrics { } /// Nested message and enum types in `ActionSummary`. pub mod action_summary { - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionData { #[prost(string, tag = "1")] pub mnemonic: ::prost::alloc::string::String, @@ -1154,7 +1154,7 @@ pub mod build_metrics { #[prost(int64, tag = "7")] pub actions_created: i64, } - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RunnerCount { #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, @@ -1183,7 +1183,7 @@ pub mod build_metrics { } /// Nested message and enum types in `MemoryMetrics`. pub mod memory_metrics { - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GarbageMetrics { /// Type of garbage collected, e.g. G1 Old Gen. #[prost(string, tag = "1")] @@ -1194,7 +1194,7 @@ pub mod build_metrics { pub garbage_collected: i64, } } - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetMetrics { /// DEPRECATED /// No longer populated. It never measured what it was supposed to (targets @@ -1238,7 +1238,7 @@ pub mod build_metrics { super::super::devtools::build::lib::packages::metrics::PackageLoadMetrics, >, } - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TimingMetrics { /// For Skymeld, it's possible that /// analysis_phase_time_in_ms + execution_phase_time_in_ms >= wall_time_in_ms @@ -1265,7 +1265,7 @@ pub mod build_metrics { #[prost(int64, tag = "5")] pub actions_execution_start_in_ms: i64, } - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CumulativeMetrics { /// One-indexed number of "analyses" the server has run, including the /// current one. Will be incremented for every build/test/cquery/etc. command @@ -1278,7 +1278,7 @@ pub mod build_metrics { #[prost(int32, tag = "12")] pub num_builds: i32, } - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ArtifactMetrics { /// Measures all source files newly read this build. Does not include /// unchanged sources on incremental builds. @@ -1305,7 +1305,7 @@ pub mod build_metrics { } /// Nested message and enum types in `ArtifactMetrics`. pub mod artifact_metrics { - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct FilesMetric { #[prost(int64, tag = "1")] pub size_in_bytes: i64, @@ -1314,7 +1314,7 @@ pub mod build_metrics { } } /// Data about the evaluation of Skyfunctions. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct EvaluationStat { /// Name of the Skyfunction. #[prost(string, tag = "1")] @@ -1405,7 +1405,7 @@ pub mod build_metrics { /// For SkyKeys in 'done values' where the SkyValue is of type /// RuleConfiguredTargetValue, we pull those out separately and report the /// ruleClass and action count. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RuleClassCount { /// Unique key for the rule class. #[prost(string, tag = "1")] @@ -1421,7 +1421,7 @@ pub mod build_metrics { pub action_count: u64, } /// For SkyKeys whose function name is ASPECT break out that information - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct AspectCount { /// Unique key for Aspect. #[prost(string, tag = "1")] @@ -1486,7 +1486,7 @@ pub mod build_metrics { /// Nested message and enum types in `WorkerMetrics`. pub mod worker_metrics { /// Information collected from worker at some point. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct WorkerStats { /// Epoch unix time of collection of metrics. #[prost(int64, tag = "1")] @@ -1570,7 +1570,7 @@ pub mod build_metrics { } } /// Information about host network. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct NetworkMetrics { #[prost(message, optional, tag = "1")] pub system_network_stats: ::core::option::Option< @@ -1581,7 +1581,7 @@ pub mod build_metrics { pub mod network_metrics { /// Information for all the network traffic going on on the host machine /// during the invocation. - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct SystemNetworkStats { /// Total bytes sent during the invocation. #[prost(uint64, tag = "1")] @@ -1621,7 +1621,7 @@ pub mod build_metrics { } /// Nested message and enum types in `WorkerPoolMetrics`. pub mod worker_pool_metrics { - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WorkerPoolStats { /// Hash of worker pool these stats are for. Contains information about /// startup flags. @@ -1669,7 +1669,7 @@ pub mod build_metrics { } /// Nested message and enum types in `DynamicExecutionMetrics`. pub mod dynamic_execution_metrics { - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RaceStatistics { /// Mnemonic of the action. #[prost(string, tag = "1")] @@ -1708,7 +1708,7 @@ pub struct ConvenienceSymlinksIdentified { } /// The message that contains what type of action to perform on a given path and /// target of a symlink. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConvenienceSymlink { /// The path of the symlink to be created or deleted, absolute or relative to /// the workspace, creating any directories necessary. If a symlink already @@ -1787,7 +1787,7 @@ pub struct ExecRequestConstructed { pub should_exec: bool, } /// An environment variable provided by a run command after a successful build. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct EnvironmentVariable { #[prost(bytes = "bytes", tag = "1")] pub name: ::prost::bytes::Bytes, diff --git a/nativelink-proto/genproto/com.github.trace_machina.nativelink.events.pb.rs b/nativelink-proto/genproto/com.github.trace_machina.nativelink.events.pb.rs index f433da4fa..30d2d9bb4 100644 --- a/nativelink-proto/genproto/com.github.trace_machina.nativelink.events.pb.rs +++ b/nativelink-proto/genproto/com.github.trace_machina.nativelink.events.pb.rs @@ -29,7 +29,7 @@ pub struct BatchUpdateBlobsRequestOverride { } /// Nested message and enum types in `BatchUpdateBlobsRequestOverride`. pub mod batch_update_blobs_request_override { - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Request { #[prost(message, optional, tag = "1")] pub digest: ::core::option::Option< @@ -82,7 +82,7 @@ pub mod batch_read_blobs_response_override { } /// / Same as google.bytestream.WriteRequest, but without the data field, /// / and add a `data_len` field. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WriteRequestOverride { #[prost(string, tag = "1")] pub resource_name: ::prost::alloc::string::String, @@ -299,7 +299,7 @@ pub struct OriginEvents { pub events: ::prost::alloc::vec::Vec, } /// / Bep event that has occurred. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BepEvent { /// / The version of this message. #[prost(uint32, tag = "1")] @@ -316,7 +316,7 @@ pub struct BepEvent { /// Nested message and enum types in `BepEvent`. pub mod bep_event { /// / The event that occurred. - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Event { #[prost(message, tag = "3")] LifecycleEvent( diff --git a/nativelink-proto/genproto/com.github.trace_machina.nativelink.remote_execution.pb.rs b/nativelink-proto/genproto/com.github.trace_machina.nativelink.remote_execution.pb.rs index e5295d0a8..7eba7cf68 100644 --- a/nativelink-proto/genproto/com.github.trace_machina.nativelink.remote_execution.pb.rs +++ b/nativelink-proto/genproto/com.github.trace_machina.nativelink.remote_execution.pb.rs @@ -14,10 +14,10 @@ // This file is @generated by prost-build. /// / Request object for keep alive requests. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct KeepAliveRequest {} /// / Request object for going away requests. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct GoingAwayRequest {} /// / Represents the initial request sent to the scheduler informing the /// / scheduler about this worker's capabilities and metadata. @@ -83,14 +83,14 @@ pub mod execute_result { } } /// / The result of an ExecutionComplete. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecuteComplete { /// / The operation ID that was executed. #[prost(string, tag = "1")] pub operation_id: ::prost::alloc::string::String, } /// / Resource usage observed by the worker while running one action. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionResourceUsage { /// / Peak resident memory observed for the action process tree. #[prost(uint64, tag = "1")] @@ -106,14 +106,14 @@ pub struct ActionResourceUsage { pub worker_id: ::prost::alloc::string::String, } /// / Result sent back from the server when a node connects. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConnectionResult { /// / The worker ID to place in the action results generated by this worker. #[prost(string, tag = "1")] pub worker_id: ::prost::alloc::string::String, } /// / Request to kill a running operation sent from the scheduler to a worker. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct KillOperationRequest { /// / The the operation id for the operation to be killed. #[prost(string, tag = "1")] @@ -347,7 +347,7 @@ pub mod worker_api_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/com.github.trace_machina.nativelink.remote_execution.WorkerApi/ConnectWorker", ); @@ -515,7 +515,7 @@ pub mod worker_api_server { let inner = self.inner.clone(); let fut = async move { let method = ConnectWorkerSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, diff --git a/nativelink-proto/genproto/command_line.pb.rs b/nativelink-proto/genproto/command_line.pb.rs index cea8bfc8a..7cc467eb2 100644 --- a/nativelink-proto/genproto/command_line.pb.rs +++ b/nativelink-proto/genproto/command_line.pb.rs @@ -54,7 +54,7 @@ pub mod command_line_section { } } /// Wrapper to allow a list of strings in the "oneof" section_type. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ChunkList { #[prost(string, repeated, tag = "1")] pub chunk: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, @@ -74,7 +74,7 @@ pub struct OptionList { /// represents the canonical form of the command line, with the values as Bazel /// understands them, then the expansion flag, which has no value, would not /// appear, and the flags it expands to would. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Option { /// How the option looks with the option and its value combined. Depending on /// the purpose of this command line report, this could be the canonical diff --git a/nativelink-proto/genproto/devtools.build.lib.packages.metrics.pb.rs b/nativelink-proto/genproto/devtools.build.lib.packages.metrics.pb.rs index f82bdb84f..43a8979be 100644 --- a/nativelink-proto/genproto/devtools.build.lib.packages.metrics.pb.rs +++ b/nativelink-proto/genproto/devtools.build.lib.packages.metrics.pb.rs @@ -14,7 +14,7 @@ // This file is @generated by prost-build. /// Message used to concisely report all package metrics. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PackageLoadMetrics { /// Name of the package. #[prost(string, optional, tag = "1")] diff --git a/nativelink-proto/genproto/failure_details.pb.rs b/nativelink-proto/genproto/failure_details.pb.rs index f890f9d85..1c7192545 100644 --- a/nativelink-proto/genproto/failure_details.pb.rs +++ b/nativelink-proto/genproto/failure_details.pb.rs @@ -13,7 +13,7 @@ // limitations under the License. // This file is @generated by prost-build. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct FailureDetailMetadata { #[prost(uint32, tag = "1")] pub exit_code: u32, @@ -209,7 +209,7 @@ pub mod failure_detail { RemoteAnalysisCaching(super::RemoteAnalysisCaching), } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Interrupted { #[prost(enumeration = "interrupted::Code", tag = "1")] pub code: i32, @@ -307,7 +307,7 @@ pub mod interrupted { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Spawn { #[prost(enumeration = "spawn::Code", tag = "1")] pub code: i32, @@ -424,7 +424,7 @@ pub mod spawn { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExternalRepository { /// Additional data could include external repository names. #[prost(enumeration = "external_repository::Code", tag = "1")] @@ -486,7 +486,7 @@ pub mod external_repository { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildProgress { /// Additional data could include the build progress upload endpoint. #[prost(enumeration = "build_progress::Code", tag = "1")] @@ -598,7 +598,7 @@ pub mod build_progress { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct RemoteOptions { #[prost(enumeration = "remote_options::Code", tag = "1")] pub code: i32, @@ -660,7 +660,7 @@ pub mod remote_options { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ClientEnvironment { #[prost(enumeration = "client_environment::Code", tag = "1")] pub code: i32, @@ -757,7 +757,7 @@ pub mod crash { } } } -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Throwable { /// The class name of the java.lang.Throwable. #[prost(string, tag = "1")] @@ -771,7 +771,7 @@ pub struct Throwable { #[prost(string, repeated, tag = "3")] pub stack_trace: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct SymlinkForest { #[prost(enumeration = "symlink_forest::Code", tag = "1")] pub code: i32, @@ -827,7 +827,7 @@ pub mod symlink_forest { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildReport { /// Additional data for partial failures might include the build report that /// failed to be written. @@ -881,7 +881,7 @@ pub mod build_report { } } /// Failure details for errors produced when using Skyfocus -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Skyfocus { #[prost(enumeration = "skyfocus::Code", tag = "1")] pub code: i32, @@ -940,7 +940,7 @@ pub mod skyfocus { } } /// Failure details for errors produced during remote analysis caching. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct RemoteAnalysisCaching { #[prost(enumeration = "remote_analysis_caching::Code", tag = "1")] pub code: i32, @@ -993,7 +993,7 @@ pub mod remote_analysis_caching { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct PackageOptions { #[prost(enumeration = "package_options::Code", tag = "1")] pub code: i32, @@ -1040,7 +1040,7 @@ pub mod package_options { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct RemoteExecution { #[prost(enumeration = "remote_execution::Code", tag = "1")] pub code: i32, @@ -1166,7 +1166,7 @@ pub mod remote_execution { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Execution { #[prost(enumeration = "execution::Code", tag = "1")] pub code: i32, @@ -1403,7 +1403,7 @@ pub mod execution { } } /// Failure details about Bazel's WORKSPACE features. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Workspaces { #[prost(enumeration = "workspaces::Code", tag = "1")] pub code: i32, @@ -1470,7 +1470,7 @@ pub mod workspaces { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CrashOptions { #[prost(enumeration = "crash_options::Code", tag = "1")] pub code: i32, @@ -1511,7 +1511,7 @@ pub mod crash_options { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Filesystem { #[prost(enumeration = "filesystem::Code", tag = "1")] pub code: i32, @@ -1577,7 +1577,7 @@ pub mod filesystem { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExecutionOptions { #[prost(enumeration = "execution_options::Code", tag = "1")] pub code: i32, @@ -1671,7 +1671,7 @@ pub mod execution_options { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Command { #[prost(enumeration = "command::Code", tag = "1")] pub code: i32, @@ -1770,7 +1770,7 @@ pub mod command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct GrpcServer { #[prost(enumeration = "grpc_server::Code", tag = "1")] pub code: i32, @@ -1823,7 +1823,7 @@ pub mod grpc_server { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CanonicalizeFlags { #[prost(enumeration = "canonicalize_flags::Code", tag = "1")] pub code: i32, @@ -1872,7 +1872,7 @@ pub mod canonicalize_flags { /// intended as a grab-bag for all Bazel flag value constraint violations, which /// instead generally belong in the category for the subsystem whose flag values /// participate in the constraint. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildConfiguration { #[prost(enumeration = "build_configuration::Code", tag = "1")] pub code: i32, @@ -1983,7 +1983,7 @@ pub mod build_configuration { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct InfoCommand { #[prost(enumeration = "info_command::Code", tag = "1")] pub code: i32, @@ -2039,7 +2039,7 @@ pub mod info_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct MemoryOptions { #[prost(enumeration = "memory_options::Code", tag = "1")] pub code: i32, @@ -2061,8 +2061,10 @@ pub mod memory_options { pub enum Code { MemoryOptionsUnknown = 0, /// Deprecated: validation is now implemented by the option converter. + #[deprecated] DeprecatedExperimentalOomMoreEagerlyThresholdInvalidValue = 1, /// Deprecated: no tenured collectors found is now a crash on startup. + #[deprecated] DeprecatedExperimentalOomMoreEagerlyNoTenuredCollectorsFound = 2, } impl Code { @@ -2073,9 +2075,11 @@ pub mod memory_options { pub fn as_str_name(&self) -> &'static str { match self { Self::MemoryOptionsUnknown => "MEMORY_OPTIONS_UNKNOWN", + #[allow(deprecated)] Self::DeprecatedExperimentalOomMoreEagerlyThresholdInvalidValue => { "DEPRECATED_EXPERIMENTAL_OOM_MORE_EAGERLY_THRESHOLD_INVALID_VALUE" } + #[allow(deprecated)] Self::DeprecatedExperimentalOomMoreEagerlyNoTenuredCollectorsFound => { "DEPRECATED_EXPERIMENTAL_OOM_MORE_EAGERLY_NO_TENURED_COLLECTORS_FOUND" } @@ -2086,10 +2090,14 @@ pub mod memory_options { match value { "MEMORY_OPTIONS_UNKNOWN" => Some(Self::MemoryOptionsUnknown), "DEPRECATED_EXPERIMENTAL_OOM_MORE_EAGERLY_THRESHOLD_INVALID_VALUE" => { - Some(Self::DeprecatedExperimentalOomMoreEagerlyThresholdInvalidValue) + Some( + #[allow(deprecated)] + Self::DeprecatedExperimentalOomMoreEagerlyThresholdInvalidValue, + ) } "DEPRECATED_EXPERIMENTAL_OOM_MORE_EAGERLY_NO_TENURED_COLLECTORS_FOUND" => { Some( + #[allow(deprecated)] Self::DeprecatedExperimentalOomMoreEagerlyNoTenuredCollectorsFound, ) } @@ -2098,7 +2106,7 @@ pub mod memory_options { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Query { #[prost(enumeration = "query::Code", tag = "1")] pub code: i32, @@ -2294,7 +2302,7 @@ pub mod query { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct LocalExecution { #[prost(enumeration = "local_execution::Code", tag = "1")] pub code: i32, @@ -2341,7 +2349,7 @@ pub mod local_execution { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionCache { #[prost(enumeration = "action_cache::Code", tag = "1")] pub code: i32, @@ -2385,7 +2393,7 @@ pub mod action_cache { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct FetchCommand { #[prost(enumeration = "fetch_command::Code", tag = "1")] pub code: i32, @@ -2438,7 +2446,7 @@ pub mod fetch_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct SyncCommand { #[prost(enumeration = "sync_command::Code", tag = "1")] pub code: i32, @@ -2491,7 +2499,7 @@ pub mod sync_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Sandbox { #[prost(enumeration = "sandbox::Code", tag = "1")] pub code: i32, @@ -2577,7 +2585,7 @@ pub mod sandbox { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct IncludeScanning { #[prost(enumeration = "include_scanning::Code", tag = "1")] pub code: i32, @@ -2657,7 +2665,7 @@ pub mod include_scanning { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestCommand { #[prost(enumeration = "test_command::Code", tag = "1")] pub code: i32, @@ -2707,7 +2715,7 @@ pub mod test_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionQuery { #[prost(enumeration = "action_query::Code", tag = "1")] pub code: i32, @@ -2815,7 +2823,7 @@ pub mod action_query { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TargetPatterns { #[prost(enumeration = "target_patterns::Code", tag = "1")] pub code: i32, @@ -2937,7 +2945,7 @@ pub mod target_patterns { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CleanCommand { #[prost(enumeration = "clean_command::Code", tag = "1")] pub code: i32, @@ -3010,7 +3018,7 @@ pub mod clean_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConfigCommand { #[prost(enumeration = "config_command::Code", tag = "1")] pub code: i32, @@ -3057,7 +3065,7 @@ pub mod config_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConfigurableQuery { #[prost(enumeration = "configurable_query::Code", tag = "1")] pub code: i32, @@ -3147,7 +3155,7 @@ pub mod configurable_query { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct DumpCommand { #[prost(enumeration = "dump_command::Code", tag = "1")] pub code: i32, @@ -3208,7 +3216,7 @@ pub mod dump_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct HelpCommand { #[prost(enumeration = "help_command::Code", tag = "1")] pub code: i32, @@ -3255,7 +3263,7 @@ pub mod help_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct MobileInstall { #[prost(enumeration = "mobile_install::Code", tag = "1")] pub code: i32, @@ -3314,7 +3322,7 @@ pub mod mobile_install { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ProfileCommand { #[prost(enumeration = "profile_command::Code", tag = "1")] pub code: i32, @@ -3361,7 +3369,7 @@ pub mod profile_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct RunCommand { #[prost(enumeration = "run_command::Code", tag = "1")] pub code: i32, @@ -3471,7 +3479,7 @@ pub mod run_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct VersionCommand { #[prost(enumeration = "version_command::Code", tag = "1")] pub code: i32, @@ -3515,7 +3523,7 @@ pub mod version_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct PrintActionCommand { #[prost(enumeration = "print_action_command::Code", tag = "1")] pub code: i32, @@ -3570,7 +3578,7 @@ pub mod print_action_command { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct WorkspaceStatus { #[prost(enumeration = "workspace_status::Code", tag = "1")] pub code: i32, @@ -3632,7 +3640,7 @@ pub mod workspace_status { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct JavaCompile { #[prost(enumeration = "java_compile::Code", tag = "1")] pub code: i32, @@ -3691,7 +3699,7 @@ pub mod java_compile { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ActionRewinding { #[prost(enumeration = "action_rewinding::Code", tag = "1")] pub code: i32, @@ -3718,6 +3726,7 @@ pub mod action_rewinding { LostInputRewindingDisabled = 5, LostOutputRewindingDisabled = 6, /// Deprecated: attempting to rewind a source artifact is now a hard crash. + #[deprecated] DeprecatedLostInputIsSource = 2, } impl Code { @@ -3733,6 +3742,7 @@ pub mod action_rewinding { Self::LostOutputTooManyTimes => "LOST_OUTPUT_TOO_MANY_TIMES", Self::LostInputRewindingDisabled => "LOST_INPUT_REWINDING_DISABLED", Self::LostOutputRewindingDisabled => "LOST_OUTPUT_REWINDING_DISABLED", + #[allow(deprecated)] Self::DeprecatedLostInputIsSource => "DEPRECATED_LOST_INPUT_IS_SOURCE", } } @@ -3750,14 +3760,14 @@ pub mod action_rewinding { Some(Self::LostOutputRewindingDisabled) } "DEPRECATED_LOST_INPUT_IS_SOURCE" => { - Some(Self::DeprecatedLostInputIsSource) + Some(#[allow(deprecated)] Self::DeprecatedLostInputIsSource) } _ => None, } } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CppCompile { #[prost(enumeration = "cpp_compile::Code", tag = "1")] pub code: i32, @@ -3839,7 +3849,7 @@ pub mod cpp_compile { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct StarlarkAction { #[prost(enumeration = "starlark_action::Code", tag = "1")] pub code: i32, @@ -3890,7 +3900,7 @@ pub mod starlark_action { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct NinjaAction { #[prost(enumeration = "ninja_action::Code", tag = "1")] pub code: i32, @@ -3941,7 +3951,7 @@ pub mod ninja_action { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct DynamicExecution { #[prost(enumeration = "dynamic_execution::Code", tag = "1")] pub code: i32, @@ -3994,7 +4004,7 @@ pub mod dynamic_execution { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct FailAction { #[prost(enumeration = "fail_action::Code", tag = "1")] pub code: i32, @@ -4061,7 +4071,7 @@ pub mod fail_action { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct SymlinkAction { #[prost(enumeration = "symlink_action::Code", tag = "1")] pub code: i32, @@ -4124,7 +4134,7 @@ pub mod symlink_action { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CppLink { #[prost(enumeration = "cpp_link::Code", tag = "1")] pub code: i32, @@ -4173,7 +4183,7 @@ pub mod cpp_link { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct LtoAction { #[prost(enumeration = "lto_action::Code", tag = "1")] pub code: i32, @@ -4225,7 +4235,7 @@ pub mod lto_action { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct TestAction { #[prost(enumeration = "test_action::Code", tag = "1")] pub code: i32, @@ -4283,7 +4293,7 @@ pub mod test_action { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Worker { #[prost(enumeration = "worker::Code", tag = "1")] pub code: i32, @@ -4371,7 +4381,7 @@ pub mod worker { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Analysis { #[prost(enumeration = "analysis::Code", tag = "1")] pub code: i32, @@ -4493,7 +4503,7 @@ pub mod analysis { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct PackageLoading { #[prost(enumeration = "package_loading::Code", tag = "1")] pub code: i32, @@ -4654,7 +4664,7 @@ pub mod package_loading { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct Toolchain { #[prost(enumeration = "toolchain::Code", tag = "1")] pub code: i32, @@ -4718,7 +4728,7 @@ pub mod toolchain { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct StarlarkLoading { #[prost(enumeration = "starlark_loading::Code", tag = "1")] pub code: i32, @@ -4791,7 +4801,7 @@ pub mod starlark_loading { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExternalDeps { #[prost(enumeration = "external_deps::Code", tag = "1")] pub code: i32, @@ -4853,7 +4863,7 @@ pub mod external_deps { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct DiffAwareness { #[prost(enumeration = "diff_awareness::Code", tag = "1")] pub code: i32, @@ -4897,7 +4907,7 @@ pub mod diff_awareness { } } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ModCommand { #[prost(enumeration = "mod_command::Code", tag = "1")] pub code: i32, diff --git a/nativelink-proto/genproto/google.api.pb.rs b/nativelink-proto/genproto/google.api.pb.rs index 280cb2dc9..2895fd42d 100644 --- a/nativelink-proto/genproto/google.api.pb.rs +++ b/nativelink-proto/genproto/google.api.pb.rs @@ -353,7 +353,7 @@ pub mod http_rule { /// Determines the URL pattern is matched by this rules. This pattern can be /// used with any of the {get|put|post|delete|patch} methods. A custom method /// can be defined using the 'custom' field. - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Pattern { /// Maps to HTTP GET. Used for listing and getting information about /// resources. @@ -380,7 +380,7 @@ pub mod http_rule { } } /// A custom pattern is used for defining custom HTTP verb. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct CustomHttpPattern { /// The name of this custom HTTP verb. #[prost(string, tag = "1")] diff --git a/nativelink-proto/genproto/google.bytestream.pb.rs b/nativelink-proto/genproto/google.bytestream.pb.rs index d0229a041..f6fc14a0e 100644 --- a/nativelink-proto/genproto/google.bytestream.pb.rs +++ b/nativelink-proto/genproto/google.bytestream.pb.rs @@ -14,7 +14,7 @@ // This file is @generated by prost-build. /// Request object for ByteStream.Read. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ReadRequest { /// The name of the resource to read. #[prost(string, tag = "1")] @@ -38,7 +38,7 @@ pub struct ReadRequest { } /// Response object for ByteStream.Read. #[derive(::derive_more::Debug)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] #[prost(skip_debug)] pub struct ReadResponse { /// A portion of the data for the resource. The service **may** leave `data` @@ -51,7 +51,7 @@ pub struct ReadResponse { } /// Request object for ByteStream.Write. #[derive(::derive_more::Debug)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] #[prost(skip_debug)] pub struct WriteRequest { /// The name of the resource to write. This **must** be set on the first @@ -87,21 +87,21 @@ pub struct WriteRequest { pub data: ::prost::bytes::Bytes, } /// Response object for ByteStream.Write. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct WriteResponse { /// The number of bytes that have been processed for the given resource. #[prost(int64, tag = "1")] pub committed_size: i64, } /// Request object for ByteStream.QueryWriteStatus. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct QueryWriteStatusRequest { /// The name of the resource whose write status is being requested. #[prost(string, tag = "1")] pub resource_name: ::prost::alloc::string::String, } /// Response object for ByteStream.QueryWriteStatus. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct QueryWriteStatusResponse { /// The number of bytes that have been processed for the given resource. #[prost(int64, tag = "1")] @@ -232,7 +232,7 @@ pub mod byte_stream_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.bytestream.ByteStream/Read", ); @@ -275,7 +275,7 @@ pub mod byte_stream_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.bytestream.ByteStream/Write", ); @@ -313,7 +313,7 @@ pub mod byte_stream_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.bytestream.ByteStream/QueryWriteStatus", ); @@ -530,7 +530,7 @@ pub mod byte_stream_server { let inner = self.inner.clone(); let fut = async move { let method = ReadSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -577,7 +577,7 @@ pub mod byte_stream_server { let inner = self.inner.clone(); let fut = async move { let method = WriteSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -622,7 +622,7 @@ pub mod byte_stream_server { let inner = self.inner.clone(); let fut = async move { let method = QueryWriteStatusSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, diff --git a/nativelink-proto/genproto/google.devtools.build.v1.pb.rs b/nativelink-proto/genproto/google.devtools.build.v1.pb.rs index 94d70d8f6..4f6918647 100644 --- a/nativelink-proto/genproto/google.devtools.build.v1.pb.rs +++ b/nativelink-proto/genproto/google.devtools.build.v1.pb.rs @@ -14,7 +14,7 @@ // This file is @generated by prost-build. /// Status used for both invocation attempt and overall build completion. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildStatus { /// The end result. #[prost(enumeration = "build_status::Result", tag = "1")] @@ -106,7 +106,7 @@ pub mod build_status { } /// An event representing some state change that occurred in the build. This /// message does not include field for uniquely identifying an event. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildEvent { /// This should be precisely the time when this event happened, and not when /// the event proto was created or sent. @@ -121,7 +121,7 @@ pub struct BuildEvent { /// Nested message and enum types in `BuildEvent`. pub mod build_event { /// Notification that the build system has attempted to run the build tool. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct InvocationAttemptStarted { /// The number of the invocation attempt, starting at 1 and increasing by 1 /// for each new attempt. Can be used to determine if there is a later @@ -133,7 +133,7 @@ pub mod build_event { pub details: ::core::option::Option<::prost_types::Any>, } /// Notification that an invocation attempt has finished. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct InvocationAttemptFinished { /// Final status of the invocation. #[prost(message, optional, tag = "3")] @@ -143,7 +143,7 @@ pub mod build_event { pub details: ::core::option::Option<::prost_types::Any>, } /// Notification that the build request is enqueued. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildEnqueued { /// Additional details about the Build. #[prost(message, optional, tag = "1")] @@ -152,7 +152,7 @@ pub mod build_event { /// Notification that the build request has finished, and no further /// invocations will occur. Note that this applies to the entire Build. /// Individual invocations trigger InvocationFinished when they finish. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildFinished { /// Final status of the build. #[prost(message, optional, tag = "1")] @@ -162,7 +162,7 @@ pub mod build_event { pub details: ::core::option::Option<::prost_types::Any>, } /// Textual output written to standard output or standard error. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ConsoleOutput { /// The output stream type. #[prost(enumeration = "super::ConsoleOutputStream", tag = "1")] @@ -174,7 +174,7 @@ pub mod build_event { /// Nested message and enum types in `ConsoleOutput`. pub mod console_output { /// The output stream content. - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Output { /// Regular UTF-8 output; normal text. #[prost(string, tag = "2")] @@ -186,7 +186,7 @@ pub mod build_event { } /// Notification of the end of a build event stream published by a build /// component other than CONTROLLER (See StreamId.BuildComponents). - #[derive(Clone, Copy, PartialEq, ::prost::Message)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct BuildComponentStreamFinished { /// How the event stream finished. #[prost(enumeration = "build_component_stream_finished::FinishType", tag = "1")] @@ -244,7 +244,7 @@ pub mod build_event { /// ////////////////////////////////////////////////////////////////////////// /// Events that indicate a state change of a build request in the build /// queue. - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Event { /// An invocation attempt has started. #[prost(message, tag = "51")] @@ -280,7 +280,7 @@ pub mod build_event { } } /// Unique identifier for a build event stream. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct StreamId { /// The id of a Build message. #[prost(string, tag = "1")] @@ -383,7 +383,7 @@ impl ConsoleOutputStream { /// multiple invocations for a build (e.g. retries). /// - InvocationAttemptCompleted: When work for a build finishes. /// - BuildFinished: When a build is finished. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PublishLifecycleEventRequest { /// The interactivity of this build. #[prost(enumeration = "publish_lifecycle_event_request::ServiceLevel", tag = "1")] @@ -466,7 +466,7 @@ pub mod publish_lifecycle_event_request { } /// States which event has been committed. Any failure to commit will cause /// RPC errors, hence not recorded by this proto. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PublishBuildToolEventStreamResponse { /// The stream that contains this event. #[prost(message, optional, tag = "1")] @@ -477,7 +477,7 @@ pub struct PublishBuildToolEventStreamResponse { } /// Build event with contextual information about the stream it belongs to and /// its position in that stream. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct OrderedBuildEvent { /// Which build event stream this event belongs to. #[prost(message, optional, tag = "1")] @@ -492,7 +492,7 @@ pub struct OrderedBuildEvent { pub event: ::core::option::Option, } /// Streaming request message for PublishBuildToolEventStream. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PublishBuildToolEventStreamRequest { /// Required. The build event with position info. /// New publishing clients should use this field rather than the 3 above. @@ -633,7 +633,7 @@ pub mod publish_build_event_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.devtools.build.v1.PublishBuildEvent/PublishLifecycleEvent", ); @@ -668,7 +668,7 @@ pub mod publish_build_event_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.devtools.build.v1.PublishBuildEvent/PublishBuildToolEventStream", ); @@ -857,7 +857,7 @@ pub mod publish_build_event_server { let inner = self.inner.clone(); let fut = async move { let method = PublishLifecycleEventSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -912,7 +912,7 @@ pub mod publish_build_event_server { let inner = self.inner.clone(); let fut = async move { let method = PublishBuildToolEventStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, diff --git a/nativelink-proto/genproto/google.longrunning.pb.rs b/nativelink-proto/genproto/google.longrunning.pb.rs index fec578107..d5857361c 100644 --- a/nativelink-proto/genproto/google.longrunning.pb.rs +++ b/nativelink-proto/genproto/google.longrunning.pb.rs @@ -62,14 +62,14 @@ pub mod operation { } } /// The request message for [Operations.GetOperation][google.longrunning.Operations.GetOperation]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetOperationRequest { /// The name of the operation resource. #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, } /// The request message for [Operations.ListOperations][google.longrunning.Operations.ListOperations]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ListOperationsRequest { /// The name of the operation's parent resource. #[prost(string, tag = "4")] @@ -95,21 +95,21 @@ pub struct ListOperationsResponse { pub next_page_token: ::prost::alloc::string::String, } /// The request message for [Operations.CancelOperation][google.longrunning.Operations.CancelOperation]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct CancelOperationRequest { /// The name of the operation resource to be cancelled. #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, } /// The request message for [Operations.DeleteOperation][google.longrunning.Operations.DeleteOperation]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DeleteOperationRequest { /// The name of the operation resource to be deleted. #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, } /// The request message for [Operations.WaitOperation][google.longrunning.Operations.WaitOperation]. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WaitOperationRequest { /// The name of the operation resource to wait on. #[prost(string, tag = "1")] @@ -131,7 +131,7 @@ pub struct WaitOperationRequest { /// metadata_type: "LongRunningRecognizeMetadata" /// }; /// } -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct OperationInfo { /// Required. The message name of the primary return type for this /// long-running operation. @@ -267,7 +267,7 @@ pub mod operations_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.longrunning.Operations/ListOperations", ); @@ -293,7 +293,7 @@ pub mod operations_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.longrunning.Operations/GetOperation", ); @@ -320,7 +320,7 @@ pub mod operations_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.longrunning.Operations/DeleteOperation", ); @@ -353,7 +353,7 @@ pub mod operations_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.longrunning.Operations/CancelOperation", ); @@ -385,7 +385,7 @@ pub mod operations_client { format!("Service was not ready: {}", e.into()), ) })?; - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( "/google.longrunning.Operations/WaitOperation", ); @@ -586,7 +586,7 @@ pub mod operations_server { let inner = self.inner.clone(); let fut = async move { let method = ListOperationsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -631,7 +631,7 @@ pub mod operations_server { let inner = self.inner.clone(); let fut = async move { let method = GetOperationSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -676,7 +676,7 @@ pub mod operations_server { let inner = self.inner.clone(); let fut = async move { let method = DeleteOperationSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -721,7 +721,7 @@ pub mod operations_server { let inner = self.inner.clone(); let fut = async move { let method = CancelOperationSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, @@ -766,7 +766,7 @@ pub mod operations_server { let inner = self.inner.clone(); let fut = async move { let method = WaitOperationSvc(inner); - let codec = tonic::codec::ProstCodec::default(); + let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( accept_compression_encodings, diff --git a/nativelink-proto/genproto/google.rpc.pb.rs b/nativelink-proto/genproto/google.rpc.pb.rs index 184db6250..199358ec1 100644 --- a/nativelink-proto/genproto/google.rpc.pb.rs +++ b/nativelink-proto/genproto/google.rpc.pb.rs @@ -104,14 +104,14 @@ pub struct ErrorInfo { /// the delay between retries based on `retry_delay`, until either a maximum /// number of retries have been reached or a maximum retry delay cap has been /// reached. -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct RetryInfo { /// Clients should wait at least this long between retrying the same request. #[prost(message, optional, tag = "1")] pub retry_delay: ::core::option::Option<::prost_types::Duration>, } /// Describes additional debugging info. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DebugInfo { /// The stack trace entries indicating where the error occurred. #[prost(string, repeated, tag = "1")] @@ -241,7 +241,7 @@ pub struct PreconditionFailure { /// Nested message and enum types in `PreconditionFailure`. pub mod precondition_failure { /// A message type used to describe a single precondition failure. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Violation { /// The type of PreconditionFailure. We recommend using a service-specific /// enum type to define the supported precondition violation subjects. For @@ -272,7 +272,7 @@ pub struct BadRequest { /// Nested message and enum types in `BadRequest`. pub mod bad_request { /// A message type used to describe a single bad request field. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct FieldViolation { /// A path that leads to a field in the request body. The value will be a /// sequence of dot-separated identifiers that identify a protocol buffer @@ -332,7 +332,7 @@ pub mod bad_request { } /// Contains metadata about the request that clients can attach when filing a bug /// or providing other forms of feedback. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct RequestInfo { /// An opaque string that should only be interpreted by the service generating /// it. For example, it can be used to identify requests in the service's logs. @@ -344,7 +344,7 @@ pub struct RequestInfo { pub serving_data: ::prost::alloc::string::String, } /// Describes the resource that is being accessed. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ResourceInfo { /// A name for the type of resource being accessed, e.g. "sql table", /// "cloud storage bucket", "file", "Google calendar"; or the type URL @@ -382,7 +382,7 @@ pub struct Help { /// Nested message and enum types in `Help`. pub mod help { /// Describes a URL link. - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Link { /// Describes what the link offers. #[prost(string, tag = "1")] @@ -394,7 +394,7 @@ pub mod help { } /// Provides a localized error message that is safe to return to the user /// which can be attached to an RPC error. -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct LocalizedMessage { /// The locale used following the specification defined at /// diff --git a/nativelink-scheduler/Cargo.toml b/nativelink-scheduler/Cargo.toml index e57468d3c..392f5d413 100644 --- a/nativelink-scheduler/Cargo.toml +++ b/nativelink-scheduler/Cargo.toml @@ -21,13 +21,13 @@ futures = { version = "0.3.31", default-features = false } humantime = { version = "2.3.0", default-features = false } lru = { version = "0.16.0", default-features = false } mock_instant = { version = "0.5.3", default-features = false } -opentelemetry = { version = "0.30.0", default-features = false } -opentelemetry-semantic-conventions = { version = "0.30.0", default-features = false, features = [ +opentelemetry = { version = "0.32.0", default-features = false } +opentelemetry-semantic-conventions = { version = "0.32.0", default-features = false, features = [ "default", "semconv_experimental", ] } parking_lot = { version = "0.12.3", default-features = false } -prost = { version = "0.13.5", default-features = false } +prost = { version = "0.14.4", default-features = false } redis = { version = "1.0.0", default-features = false } scopeguard = { version = "1.2.0", default-features = false } serde = { version = "1.0.219", features = ["rc"], default-features = false } @@ -42,7 +42,7 @@ tokio = { version = "1.52.2", features = [ tokio-stream = { version = "0.1.17", features = [ "fs", ], default-features = false } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "tls-ring", "transport", ], default-features = false } diff --git a/nativelink-service/BUILD.bazel b/nativelink-service/BUILD.bazel index 5015732e0..2d13c305b 100644 --- a/nativelink-service/BUILD.bazel +++ b/nativelink-service/BUILD.bazel @@ -96,6 +96,7 @@ rust_test_suite( "@crates//:tokio", "@crates//:tokio-stream", "@crates//:tonic", + "@crates//:tonic-prost", "@crates//:tower", "@crates//:tracing", "@crates//:tracing-test", diff --git a/nativelink-service/Cargo.toml b/nativelink-service/Cargo.toml index 2fadad6d2..b830a129e 100644 --- a/nativelink-service/Cargo.toml +++ b/nativelink-service/Cargo.toml @@ -20,14 +20,14 @@ bytes = { version = "1.10.1", default-features = false } futures = { version = "0.3.31", default-features = false } http-body-util = { version = "0.1.3", default-features = false } hyper = { version = "1.6.0", default-features = false } -opentelemetry = { version = "0.30.0", default-features = false } -opentelemetry-semantic-conventions = { version = "0.30.0", default-features = false, features = [ +opentelemetry = { version = "0.32.0", default-features = false } +opentelemetry-semantic-conventions = { version = "0.32.0", default-features = false, features = [ "default", "semconv_experimental", ] } parking_lot = { version = "0.12.3", default-features = false } -prost = { version = "0.13.5", default-features = false } -prost-types = { version = "0.13.5", default-features = false, features = [ +prost = { version = "0.14.4", default-features = false } +prost-types = { version = "0.14.4", default-features = false, features = [ "std", ] } rand = { version = "0.9.0", default-features = false, features = [ @@ -43,7 +43,7 @@ tokio = { version = "1.52.2", features = [ tokio-stream = { version = "0.1.17", features = [ "fs", ], default-features = false } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "gzip", "router", "tls-ring", @@ -67,7 +67,6 @@ hyper-util = { version = "0.1.11", default-features = false } pretty_assertions = { version = "1.4.1", features = [ "std", ], default-features = false } -prost-types = { version = "0.13.5", default-features = false } serde_json = { version = "1.0.140", default-features = false, features = [ "std", ] } @@ -75,6 +74,7 @@ sha2 = { version = "0.10.8", default-features = false } tokio = { version = "1.52.2", features = [ "test-util", ], default-features = false } +tonic-prost = { version = "0.14.6", default-features = false } tracing-test = { version = "0.2.5", default-features = false, features = [ "no-env-filter", ] } diff --git a/nativelink-service/tests/bep_server_test.rs b/nativelink-service/tests/bep_server_test.rs index ac5b735f9..abcdff1f5 100644 --- a/nativelink-service/tests/bep_server_test.rs +++ b/nativelink-service/tests/bep_server_test.rs @@ -53,8 +53,9 @@ use nativelink_util::store_trait::{ use pretty_assertions::assert_eq; use prost::Message; use prost_types::Timestamp; -use tonic::codec::{Codec, ProstCodec}; +use tonic::codec::Codec; use tonic::{Request, Streaming, async_trait}; +use tonic_prost::ProstCodec; const BEP_STORE_NAME: &str = "main_bep"; diff --git a/nativelink-service/tests/bytestream_server_test.rs b/nativelink-service/tests/bytestream_server_test.rs index ad8189332..da564d8c1 100644 --- a/nativelink-service/tests/bytestream_server_test.rs +++ b/nativelink-service/tests/bytestream_server_test.rs @@ -47,9 +47,10 @@ use tokio::sync::mpsc::unbounded_channel; use tokio::task::yield_now; use tokio_stream::StreamExt; use tokio_stream::wrappers::UnboundedReceiverStream; -use tonic::codec::{Codec, CompressionEncoding, ProstCodec}; +use tonic::codec::{Codec, CompressionEncoding}; use tonic::transport::{Channel, Endpoint}; use tonic::{Request, Response, Streaming}; +use tonic_prost::ProstCodec; use tower::service_fn; const INSTANCE_NAME: &str = "foo_instance_name"; @@ -855,7 +856,7 @@ pub async fn read_with_not_found_does_not_deadlock() -> Result<(), Error> { let result_fut = read_stream.next(); let result = result_fut.await.err_tip(|| "Expected result to be ready")?; - let expected_err_str = "status: NotFound, message: \"Key Digest(DigestInfo(\\\"0123456789abcdef000000000000000000000000000000000123456789abcdef-55\\\")) not found\", details: [], metadata: MetadataMap { headers: {} }"; + let expected_err_str = "code: 'Some requested entity was not found', message: \"Key Digest(DigestInfo(\\\"0123456789abcdef000000000000000000000000000000000123456789abcdef-55\\\")) not found\""; assert_eq!( Error::from(result.unwrap_err()), make_err!(Code::NotFound, "{expected_err_str}"), diff --git a/nativelink-store/Cargo.toml b/nativelink-store/Cargo.toml index c9c30a3c8..4d62f6fca 100644 --- a/nativelink-store/Cargo.toml +++ b/nativelink-store/Cargo.toml @@ -70,13 +70,13 @@ mongodb = { version = "3", features = [ "compat-3-0-0", "rustls-tls", ], default-features = false } -opentelemetry = { version = "0.30.0", default-features = false } +opentelemetry = { version = "0.32.0", default-features = false } parking_lot = { version = "0.12.3", features = [ "arc_lock", "send_guard", ], default-features = false } patricia_tree = { version = "0.9.0", default-features = false } -prost = { version = "0.13.5", default-features = false } +prost = { version = "0.14.4", default-features = false } rand = { version = "0.9.0", default-features = false, features = [ "thread_rng", ] } @@ -106,7 +106,7 @@ tokio-stream = { version = "0.1.17", features = [ "fs", ], default-features = false } tokio-util = { version = "0.7.14", default-features = false } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "tls-ring", "transport", ], default-features = false } diff --git a/nativelink-test/fuzz/Cargo.lock b/nativelink-test/fuzz/Cargo.lock index 9096df349..4d675ba42 100644 --- a/nativelink-test/fuzz/Cargo.lock +++ b/nativelink-test/fuzz/Cargo.lock @@ -684,7 +684,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2", "tokio", "tower-service", "tracing", @@ -1011,7 +1011,7 @@ dependencies = [ "serde_with", "sha1", "sha2", - "socket2 0.6.4", + "socket2", "stringprep", "strsim", "take_mut", @@ -1055,6 +1055,7 @@ dependencies = [ name = "nativelink-error" version = "1.5.2" dependencies = [ + "base64", "mongodb", "nativelink-metric", "nativelink-proto", @@ -1110,6 +1111,7 @@ dependencies = [ "prost", "prost-types", "tonic", + "tonic-prost", ] [[package]] @@ -1281,9 +1283,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -1291,9 +1293,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools", @@ -1304,9 +1306,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] @@ -1378,7 +1380,7 @@ dependencies = [ "itoa", "percent-encoding", "ryu", - "socket2 0.6.4", + "socket2", "url", "xxhash-rust", ] @@ -1690,16 +1692,6 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.4" @@ -1877,7 +1869,7 @@ dependencies = [ "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.4", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -1931,9 +1923,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.13.1" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "base64", @@ -1947,8 +1939,8 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "prost", - "socket2 0.5.10", + "socket2", + "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", @@ -1958,6 +1950,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + [[package]] name = "tower" version = "0.5.3" diff --git a/nativelink-util/BUILD.bazel b/nativelink-util/BUILD.bazel index 6b2e4fdc2..ec442cdcb 100644 --- a/nativelink-util/BUILD.bazel +++ b/nativelink-util/BUILD.bazel @@ -155,6 +155,7 @@ rust_test_suite( "@crates//:tokio-stream", "@crates//:tokio-util", "@crates//:tonic", + "@crates//:tonic-prost", "@crates//:tower", "@crates//:tracing", "@crates//:tracing-test", diff --git a/nativelink-util/Cargo.toml b/nativelink-util/Cargo.toml index 5ab543189..79b1f88de 100644 --- a/nativelink-util/Cargo.toml +++ b/nativelink-util/Cargo.toml @@ -27,29 +27,29 @@ hyper-util = { version = "0.1.11", default-features = false } libc = { version = "0.2.177", default-features = false } lru = { version = "0.16.0", default-features = false } mock_instant = { version = "0.5.3", default-features = false } -opentelemetry = { version = "0.30.0", default-features = false } -opentelemetry-appender-tracing = { version = "0.30.0", default-features = false } -opentelemetry-http = { version = "0.30.0", default-features = false } -opentelemetry-otlp = { version = "0.30.0", default-features = false, features = [ +opentelemetry = { version = "0.32.0", default-features = false } +opentelemetry-appender-tracing = { version = "0.32.0", default-features = false } +opentelemetry-http = { version = "0.32.0", default-features = false } +opentelemetry-otlp = { version = "0.32.0", default-features = false, features = [ "grpc-tonic", "logs", "metrics", "trace", "zstd-tonic", ] } -opentelemetry-semantic-conventions = { version = "0.30.0", default-features = false, features = [ +opentelemetry-semantic-conventions = { version = "0.32.0", default-features = false, features = [ "default", "semconv_experimental", ] } -opentelemetry_sdk = { version = "0.30.0", default-features = false } +opentelemetry_sdk = { version = "0.32.1", default-features = false } parking_lot = { version = "0.12.3", features = [ "arc_lock", "send_guard", ], default-features = false } pin-project = { version = "1.1.10", default-features = false } pin-project-lite = { version = "0.2.16", default-features = false } -prost = { version = "0.13.5", default-features = false } -prost-types = { version = "0.13.5", default-features = false, features = [ +prost = { version = "0.14.4", default-features = false } +prost-types = { version = "0.14.4", default-features = false, features = [ "std", ] } rand = { version = "0.9.0", default-features = false, features = [ @@ -69,7 +69,7 @@ tokio-stream = { version = "0.1.17", features = [ "fs", ], default-features = false } tokio-util = { version = "0.7.14", default-features = false } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "router", "tls-native-roots", "tls-ring", @@ -77,7 +77,7 @@ tonic = { version = "0.13.0", features = [ ], default-features = false } tower = { version = "0.5.2", default-features = false } tracing = { version = "0.1.41", default-features = false } -tracing-opentelemetry = { version = "0.31.0", default-features = false, features = [ +tracing-opentelemetry = { version = "0.33.0", default-features = false, features = [ "metrics", ] } tracing-subscriber = { version = "0.3.19", features = [ @@ -86,8 +86,8 @@ tracing-subscriber = { version = "0.3.19", features = [ "json", ], default-features = false } tracing-test = { version = "0.2.5", default-features = false, features = [] } - -ginepro = { version = "0.9.3", default-features = false } +# FIXME: Replace with version once https://github.com/TrueLayer/ginepro/pull/77 is fixed +ginepro = { git = "https://github.com/mstyura/ginepro", rev = "d08cdeff6300edfb46204b3b9fbde3f3355db35f", default-features = false } url = { version = "2.5.7", default-features = false } uuid = { version = "1.16.0", default-features = false, features = [ "serde", @@ -100,7 +100,7 @@ wincode = { version = "0.5.4", default-features = false, features = ["derive"] } [dev-dependencies] anyhow = { version = "1.0.100", default-features = false } nativelink-macro = { path = "../nativelink-macro" } -opentelemetry-proto = { version = "0.30.0", default-features = false, features = [ +opentelemetry-proto = { version = "0.32.0", default-features = false, features = [ "gen-tonic", "metrics", ] } @@ -120,6 +120,7 @@ serde_json = { version = "1.0.140", default-features = false } tokio = { version = "1.52.2", features = [ "test-util", ], default-features = false } +tonic-prost = { version = "0.14.6", default-features = false } tracing-test = { version = "0.2.5", default-features = false, features = [ "no-env-filter", ] } diff --git a/nativelink-util/src/origin_event.rs b/nativelink-util/src/origin_event.rs index 723697bfa..4cb85636a 100644 --- a/nativelink-util/src/origin_event.rs +++ b/nativelink-util/src/origin_event.rs @@ -16,6 +16,7 @@ use std::sync::OnceLock; use base64::Engine; use base64::prelude::BASE64_STANDARD_NO_PAD; +use nativelink_error::Error; use nativelink_proto::build::bazel::remote::execution::v2::RequestMetadata; use nativelink_proto::com::github::trace_machina::nativelink::events::{ Event, event, request_event, response_event, stream_event, @@ -34,11 +35,11 @@ pub fn request_metadata_to_baggage(metadata: &RequestMetadata) -> String { BASE64_STANDARD_NO_PAD.encode(metadata.encode_to_vec()) } -pub fn request_metadata_from_baggage(value: &str) -> Result { +pub fn request_metadata_from_baggage(value: &str) -> Result { let decoded = BASE64_STANDARD_NO_PAD .decode(value.as_bytes()) - .map_err(|err| prost::DecodeError::new(err.to_string()))?; - RequestMetadata::decode(&*decoded) + .map_err(Error::from)?; + RequestMetadata::decode(&*decoded).map_err(Error::from) } /// Returns a unique ID for the given event. @@ -144,7 +145,7 @@ where } } -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct OriginMetadata { pub identity: String, #[serde( diff --git a/nativelink-util/tests/telemetry_test.rs b/nativelink-util/tests/telemetry_test.rs index 56d19f1bb..956f34849 100644 --- a/nativelink-util/tests/telemetry_test.rs +++ b/nativelink-util/tests/telemetry_test.rs @@ -238,7 +238,7 @@ impl Service> for TestMetricsService { ); // ProstCodec: Encode = T (sent to client), // Decode = U (received from client). - let mut grpc = tonic::server::Grpc::new(tonic::codec::ProstCodec::< + let mut grpc = tonic::server::Grpc::new(tonic_prost::ProstCodec::< ExportMetricsServiceResponse, ExportMetricsServiceRequest, >::default()); @@ -385,6 +385,7 @@ fn expected_sum_data_points() -> Vec<(String, String, Vec, String)> { value: Some(AnyValue { value: Some(any_value::Value::StringValue(value.to_string())), }), + key_strindex: 0, } ) }; diff --git a/nativelink-worker/BUILD.bazel b/nativelink-worker/BUILD.bazel index 1dbad5e5c..941a6716d 100644 --- a/nativelink-worker/BUILD.bazel +++ b/nativelink-worker/BUILD.bazel @@ -99,6 +99,7 @@ rust_test_suite( "@crates//:serial_test", "@crates//:tokio", "@crates//:tonic", + "@crates//:tonic-prost", "@crates//:tracing", "@crates//:tracing-test", "@crates//:uuid", diff --git a/nativelink-worker/Cargo.toml b/nativelink-worker/Cargo.toml index e6aebabe0..107bb0603 100644 --- a/nativelink-worker/Cargo.toml +++ b/nativelink-worker/Cargo.toml @@ -23,9 +23,9 @@ dunce = { version = "1.0.5", default-features = false } filetime = { version = "0.2.25", default-features = false } formatx = { version = "0.2.3", default-features = false } futures = { version = "0.3.31", default-features = false } -opentelemetry = { version = "0.30.0", default-features = false } +opentelemetry = { version = "0.32.0", default-features = false } parking_lot = { version = "0.12.3", default-features = false } -prost = { version = "0.13.5", default-features = false } +prost = { version = "0.14.4", default-features = false } relative-path = { version = "2.0.0", default-features = false, features = [ "alloc", "std", @@ -47,7 +47,7 @@ tokio = { version = "1.52.2", features = [ tokio-stream = { version = "0.1.17", default-features = false, features = [ "fs", ] } -tonic = { version = "0.13.0", features = [ +tonic = { version = "0.14.0", features = [ "gzip", "tls-ring", "transport", @@ -72,11 +72,12 @@ pathdiff = { version = "0.2.3", default-features = false } pretty_assertions = { version = "1.4.1", features = [ "std", ], default-features = false } -prost-types = { version = "0.13.5", default-features = false } +prost-types = { version = "0.14.4", default-features = false } serial_test = { version = "3.2.0", features = [ "async", ], default-features = false } tempfile = { version = "3.15.0", default-features = false } +tonic-prost = { version = "0.14.6", default-features = false } tracing-test = { version = "0.2.5", default-features = false, features = [ "no-env-filter", ] } diff --git a/nativelink-worker/tests/utils/local_worker_test_utils.rs b/nativelink-worker/tests/utils/local_worker_test_utils.rs index 40eb5f1b8..849432bec 100644 --- a/nativelink-worker/tests/utils/local_worker_test_utils.rs +++ b/nativelink-worker/tests/utils/local_worker_test_utils.rs @@ -37,8 +37,8 @@ use tonic::{ Streaming, codec::Codec, // Needed for .decoder(). codec::CompressionEncoding, - codec::ProstCodec, }; +use tonic_prost::ProstCodec; use tracing::debug; use super::mock_running_actions_manager::MockRunningActionsManager; From d459bd908b96f7cb09d971aeeb7406180a08de58 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:51:45 +0100 Subject: [PATCH 011/144] Update Rust crate anyhow to v1.0.103 [SECURITY] (#2494) * Update Rust crate anyhow to v1.0.103 [SECURITY] * Correct a possible failure case in flaky detect_duplicate_upload * Update MODULE.bazel.lock * Curl security upgrade to 8.5.0-2ubuntu10.10 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Tom Parker-Shemilt --- Cargo.lock | 4 ++-- MODULE.bazel.lock | 2 +- deployment-examples/docker-compose/Dockerfile | 2 +- nativelink-store/tests/filesystem_store_test.rs | 5 ++++- tools/toolchain-buck2/Dockerfile | 2 +- tools/toolchain-nativelink/Dockerfile | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8590d5a1a..9fe033363 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arc-swap" diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 7a51eee11..cf67c3255 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -786,8 +786,8 @@ "anstyle-wincon_3.0.11": "{\"dependencies\":[{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.1\"},{\"name\":\"once_cell_polyfill\",\"req\":\"^1.56.1\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_System_Console\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\">=0.60.2, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "anstyle_1.0.13": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.5\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "anstyle_1.0.14": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.23\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "anyhow_1.0.100": "{\"dependencies\":[{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.51\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "anyhow_1.0.102": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"backtrace\":[],\"default\":[\"std\"],\"std\":[]}}", + "anyhow_1.0.103": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"backtrace\":[],\"default\":[\"std\"],\"std\":[]}}", "arc-swap_1.7.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"adaptive-barrier\",\"req\":\"~1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"~0.5\"},{\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"~0.8\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"~1\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"~1\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"~0.12\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"features\":[\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.130\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.130\"}],\"features\":{\"experimental-strategies\":[],\"experimental-thread-local\":[],\"internal-test-strategies\":[],\"weak\":[]}}", "arcstr_1.2.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7.1\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1\"}],\"features\":{\"default\":[\"substr\"],\"std\":[],\"substr\":[],\"substr-usize-indices\":[\"substr\"]}}", "arrayref_0.3.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"}],\"features\":{}}", diff --git a/deployment-examples/docker-compose/Dockerfile b/deployment-examples/docker-compose/Dockerfile index 06d98d4c5..c91ae833e 100644 --- a/deployment-examples/docker-compose/Dockerfile +++ b/deployment-examples/docker-compose/Dockerfile @@ -59,7 +59,7 @@ COPY --from=builder /root/nativelink-bin /usr/local/bin/nativelink ARG ADDITIONAL_SETUP_WORKER_CMD RUN apt-get update \ - && apt-get install -y --no-install-recommends curl=8.5.0-2ubuntu10.9 \ + && apt-get install -y --no-install-recommends curl=8.5.0-2ubuntu10.10 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && bash -ueo pipefail -c "${ADDITIONAL_SETUP_WORKER_CMD}" \ diff --git a/nativelink-store/tests/filesystem_store_test.rs b/nativelink-store/tests/filesystem_store_test.rs index 3df09b9da..90b571ea8 100644 --- a/nativelink-store/tests/filesystem_store_test.rs +++ b/nativelink-store/tests/filesystem_store_test.rs @@ -1771,13 +1771,16 @@ async fn detect_duplicate_upload() -> Result<(), Error> { temp_file.write_all_buf(&mut data).await?; *entry.data_size_mut() = 10; + let arc_entry = Arc::new(entry); assert!( - check_duplicate_files(&store.get_evicting_map(), key, &Arc::new(entry)).await?, + check_duplicate_files(&store.get_evicting_map(), key, &arc_entry.clone()).await?, "Expected duplicate" ); assert!(logs_contain( "Identical files, so don't need to edit, skipping emplace" )); + // Keep it alive until here to avoid early drop and delete, which breaks the test + drop(arc_entry); Ok(()) } diff --git a/tools/toolchain-buck2/Dockerfile b/tools/toolchain-buck2/Dockerfile index 313681e8f..0e2d4bf9d 100644 --- a/tools/toolchain-buck2/Dockerfile +++ b/tools/toolchain-buck2/Dockerfile @@ -18,7 +18,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive \ apt-get install -y --no-install-recommends \ git=1:2.43.0-1ubuntu7.3 \ ca-certificates=20240203 \ - curl=8.5.0-2ubuntu10.9 \ + curl=8.5.0-2ubuntu10.10 \ python3=3.12.3-0ubuntu2.1 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ diff --git a/tools/toolchain-nativelink/Dockerfile b/tools/toolchain-nativelink/Dockerfile index 84bf8c59f..a8bb7f4ca 100644 --- a/tools/toolchain-nativelink/Dockerfile +++ b/tools/toolchain-nativelink/Dockerfile @@ -23,7 +23,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-instal gcc=4:13.2.0-7ubuntu1 \ g++=4:13.2.0-7ubuntu1 \ python3=3.12.3-0ubuntu2.1 \ - curl=8.5.0-2ubuntu10.9 \ + curl=8.5.0-2ubuntu10.10 \ ca-certificates=20240203 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* From 5516d8be1db4045e1d97bede230beab626f0f4e7 Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Fri, 3 Jul 2026 08:38:40 -0700 Subject: [PATCH 012/144] Update LRE/store docs to fix #2406 (#2498) * Update LRE/store docs to fix #2406 * fIX CI * fix comments for TODOs * Address comments --- .../vocabularies/TraceMachina/accept.txt | 7 + .vale.ini | 4 + .../examples/local_rbe_self_test.json5 | 172 +++++++++++++++ .../examples/stores-config.json5 | 60 ++++-- nativelink-config/src/stores.rs | 49 +++++ .../docs/content/docs/explanations/lre.mdx | 56 +++-- .../docs/rbe/local-remote-execution.mdx | 190 ++++++++++++++++ .../docs/content/docs/rbe/local-testing.mdx | 204 ++++++++++++++++++ web/apps/docs/content/docs/rbe/meta.json | 2 + .../docs/content/docs/rbe/nix-templates.mdx | 149 +++++++------ .../docs/reference/nativelink-config/main.mdx | 104 ++++++++- .../reference/nativelink-config/meta.json | 3 +- .../nativelink-config/store-overview.mdx | 171 +++++++++++++++ .../docs/scripts/gen-config-reference.mjs | 11 +- 14 files changed, 1073 insertions(+), 109 deletions(-) create mode 100644 nativelink-config/examples/local_rbe_self_test.json5 create mode 100644 web/apps/docs/content/docs/rbe/local-remote-execution.mdx create mode 100644 web/apps/docs/content/docs/rbe/local-testing.mdx create mode 100644 web/apps/docs/content/docs/reference/nativelink-config/store-overview.mdx diff --git a/.github/styles/config/vocabularies/TraceMachina/accept.txt b/.github/styles/config/vocabularies/TraceMachina/accept.txt index 351743ec7..67959137b 100644 --- a/.github/styles/config/vocabularies/TraceMachina/accept.txt +++ b/.github/styles/config/vocabularies/TraceMachina/accept.txt @@ -289,3 +289,10 @@ gcc [Tt]eardown repo buildbox +subgraph +genrule +hardcoded +shellexpand +pluggable +[Cc]allout +hostnames diff --git a/.vale.ini b/.vale.ini index d6a0112ca..2a5ab2484 100644 --- a/.vale.ini +++ b/.vale.ini @@ -27,6 +27,10 @@ TokenIgnores = ['"]\.\/[\w-]+\.mdx['"], (?<=\])\([^)]+\) # Ignore filenames and directory names in tree visualizations. BlockIgnores = (?s)(.*?) +# Mermaid diagram source (node ids, `subgraph`/`end` keywords) isn't prose — +# scanning it produces spurious Vale.Repetition hits on short node labels. +BlockIgnores = (?s)(.*?) + # Too harsh. The `write-good.Passive` check already covers many cases. write-good.E-Prime = NO diff --git a/nativelink-config/examples/local_rbe_self_test.json5 b/nativelink-config/examples/local_rbe_self_test.json5 new file mode 100644 index 000000000..3e3149bc6 --- /dev/null +++ b/nativelink-config/examples/local_rbe_self_test.json5 @@ -0,0 +1,172 @@ +// A complete NativeLink cluster — CAS, AC, scheduler, and one worker — in a +// single process on localhost. Everything a Bazel client needs (cache AND +// execution) is on one port; nothing here talks to any external service. +// +// Use this to prove your Bazel setup can do remote caching and remote +// execution before you ever point it at a real cluster. See +// docs/rbe/local-remote-execution for the full walkthrough. +// +// Run it: +// nativelink ./nativelink-config/examples/local_rbe_self_test.json5 +// +// Then, from a Bazel workspace: +// bazel test --remote_cache=grpc://127.0.0.1:50051 \ +// --remote_executor=grpc://127.0.0.1:50051 \ +// --remote_default_exec_properties=cpu_count=1 \ +// //some:target +{ + stores: [ + { + name: "CAS_MAIN_STORE", + filesystem: { + content_path: "/tmp/nativelink-local-rbe-test/data/content_path-cas", + temp_path: "/tmp/nativelink-local-rbe-test/data/tmp_path-cas", + eviction_policy: { + max_bytes: 2000000000, + }, + }, + }, + { + name: "AC_MAIN_STORE", + filesystem: { + content_path: "/tmp/nativelink-local-rbe-test/data/content_path-ac", + temp_path: "/tmp/nativelink-local-rbe-test/data/tmp_path-ac", + eviction_policy: { + max_bytes: 200000000, + }, + }, + }, + { + name: "WORKER_FAST_SLOW_STORE", + fast_slow: { + // "fast" must be a filesystem store — the worker hardlinks out of it + // to build each action's sandbox. + fast: { + filesystem: { + content_path: "/tmp/nativelink-local-rbe-test/data/content_path-worker", + temp_path: "/tmp/nativelink-local-rbe-test/data/tmp_path-worker", + eviction_policy: { + max_bytes: 2000000000, + }, + }, + }, + + // The worker and the client-facing CAS share one store instance, so + // whatever the worker produces is immediately visible to Bazel. + slow: { + ref_store: { + name: "CAS_MAIN_STORE", + }, + }, + }, + }, + ], + schedulers: [ + { + name: "MAIN_SCHEDULER", + simple: { + supported_platform_properties: { + cpu_count: "minimum", + OSFamily: "priority", + "container-image": "priority", + }, + }, + }, + ], + workers: [ + { + local: { + worker_api_endpoint: { + uri: "grpc://127.0.0.1:50061", + }, + cas_fast_slow_store: "WORKER_FAST_SLOW_STORE", + upload_action_result: { + ac_store: "AC_MAIN_STORE", + }, + work_directory: "/tmp/nativelink-local-rbe-test/work", + platform_properties: { + cpu_count: { + values: [ + "1", + ], + }, + OSFamily: { + values: [ + "", + ], + }, + "container-image": { + values: [ + "", + ], + }, + }, + + // use_namespaces / use_mount_namespace default to false, which is + // required here — they're Linux-only and this config is meant to + // also run on macOS. + }, + }, + ], + servers: [ + { + // Everything a Bazel client needs — cache AND execution — on one + // local endpoint. No separate/remote cache service involved. + name: "local", + listener: { + http: { + socket_address: "0.0.0.0:50051", + }, + }, + services: { + cas: [ + { + cas_store: "CAS_MAIN_STORE", + }, + ], + ac: [ + { + ac_store: "AC_MAIN_STORE", + }, + ], + bytestream: [ + { + cas_store: "CAS_MAIN_STORE", + }, + ], + execution: [ + { + cas_store: "CAS_MAIN_STORE", + scheduler: "MAIN_SCHEDULER", + }, + ], + capabilities: [ + { + remote_execution: { + scheduler: "MAIN_SCHEDULER", + }, + }, + ], + }, + }, + { + // Workers talk to this port, clients never do — keep it off the + // client-facing listener (see nativelink-config/examples/README.md). + name: "worker_api", + listener: { + http: { + socket_address: "0.0.0.0:50061", + }, + }, + services: { + worker_api: { + scheduler: "MAIN_SCHEDULER", + }, + health: {}, + }, + }, + ], + global: { + max_open_files: 24576, + }, +} diff --git a/nativelink-config/examples/stores-config.json5 b/nativelink-config/examples/stores-config.json5 index 4c6a1b7d3..0748ce406 100644 --- a/nativelink-config/examples/stores-config.json5 +++ b/nativelink-config/examples/stores-config.json5 @@ -85,6 +85,38 @@ }, { name: "6", + "experimental_cloud_object_store": { + "provider": "r2", + "account_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", + "bucket": "nativelink-cas", + "key_prefix": "test-prefix/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5 + }, + "multipart_max_concurrent_uploads": 10 + } + }, + { + name: "7", + "experimental_cloud_object_store": { + "provider": "oci", + "namespace": "your-object-storage-namespace", + "region": "us-phoenix-1", + "bucket": "nativelink-cas", + "access_key_id": "oci_access_key_id", + "secret_access_key": "oci_secret_access_key", + "key_prefix": "test-prefix/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5 + } + } + }, + { + name: "8", "ontap_s3_existence_cache": { "index_path": "/path/to/cache/index.json", "sync_interval_seconds": 300, @@ -97,7 +129,7 @@ } }, { - name: "7", + name: "9", "verify": { "backend": { "memory": { @@ -111,7 +143,7 @@ } }, { - name: "8", + name: "10", "completeness_checking": { "backend": { "filesystem": { @@ -130,7 +162,7 @@ } }, { - name: "9", + name: "11", "compression": { "compression_algorithm": { "lz4": {} @@ -147,7 +179,7 @@ } }, { - name: "10", + name: "12", "dedup": { "index_store": { "memory": { @@ -186,7 +218,7 @@ } }, { - name: "11", + name: "13", "existence_cache": { "backend": { "memory": { @@ -202,7 +234,7 @@ } }, { - name: "12", + name: "14", "fast_slow": { "fast": { "filesystem": { @@ -225,7 +257,7 @@ } }, { - name: "13", + name: "15", "shard": { "stores": [ { @@ -241,7 +273,7 @@ } }, { - name: "14", + name: "16", "filesystem": { "content_path": "/tmp/nativelink/data-worker-test/content_path-cas", "temp_path": "/tmp/nativelink/data-worker-test/tmp_path-cas", @@ -251,13 +283,13 @@ } }, { - name: "15", + name: "17", "ref_store": { "name": "FS_CONTENT_STORE" } }, { - name: "16", + name: "18", "size_partitioning": { "size": "128mib", "lower_store": { @@ -274,7 +306,7 @@ } }, { - name: "17", + name: "19", "grpc": { "instance_name": "main", "endpoints": [ @@ -295,7 +327,7 @@ } }, { - name: "18", + name: "20", "redis_store": { "addresses": [ "redis://127.0.0.1:6379/", @@ -304,11 +336,11 @@ } }, { - name: "19", + name: "21", "noop": {} }, { - name: "20", + name: "22", "experimental_mongo": { "connection_string": "mongodb://localhost:27017", "database": "nativelink", diff --git a/nativelink-config/src/stores.rs b/nativelink-config/src/stores.rs index 9cc0aff1b..e67db1bc6 100644 --- a/nativelink-config/src/stores.rs +++ b/nativelink-config/src/stores.rs @@ -181,6 +181,55 @@ pub enum StoreSpec { /// "multipart_max_concurrent_uploads": 10 /// } /// ``` + /// + /// 5. **Cloudflare R2:** + /// R2 store uses Cloudflare's R2 service as a backend. R2 speaks the + /// S3 API, so this is a thin wrapper that derives the account-scoped + /// endpoint (`https://{account_id}.r2.cloudflarestorage.com`) for you. + /// + /// **Example JSON Config:** + /// ```json + /// "experimental_cloud_object_store": { + /// "provider": "r2", + /// "account_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", + /// "bucket": "nativelink-cas", + /// "key_prefix": "test-prefix/", + /// "retry": { + /// "max_retries": 6, + /// "delay": 0.3, + /// "jitter": 0.5 + /// }, + /// "multipart_max_concurrent_uploads": 10 + /// } + /// ``` + /// + /// 6. **Oracle Cloud Infrastructure (OCI) Object Storage:** + /// OCI store uses Oracle Cloud Infrastructure's S3-compatible Object + /// Storage API. The path-style endpoint is derived from your Object + /// Storage `namespace` and `region` as + /// `https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com`. + /// Authenticate with a Customer Secret Key (Access Key/Secret Key pair + /// created under User Settings -> Customer secret keys in the OCI + /// console); the secret cannot be retrieved after generation, so read + /// it from an env var via shellexpand. + /// + /// **Example JSON Config:** + /// ```json + /// "experimental_cloud_object_store": { + /// "provider": "oci", + /// "namespace": "your-object-storage-namespace", + /// "region": "us-phoenix-1", + /// "bucket": "nativelink-cas", + /// "access_key_id": "oci_access_key_id", + /// "secret_access_key": "oci_secret_access_key", + /// "key_prefix": "test-prefix/", + /// "retry": { + /// "max_retries": 6, + /// "delay": 0.3, + /// "jitter": 0.5 + /// } + /// } + /// ``` ExperimentalCloudObjectStore(ExperimentalCloudObjectSpec), /// ONTAP S3 Existence Cache provides a caching layer on top of the ONTAP S3 store diff --git a/web/apps/docs/content/docs/explanations/lre.mdx b/web/apps/docs/content/docs/explanations/lre.mdx index 295868e8f..ff7ad1d21 100644 --- a/web/apps/docs/content/docs/explanations/lre.mdx +++ b/web/apps/docs/content/docs/explanations/lre.mdx @@ -66,39 +66,63 @@ The recommended flow: is the easiest path.
  • - **Pull the NativeLink LRE flake template.** + **Pull the NativeLink Bazel flake template.** ```bash - nix flake init -t github:TraceMachina/nativelink#lre + nix flake init -t github:TraceMachina/nativelink#bazel ```
  • - **Enter the dev shell.** This downloads the pinned toolchain on - first run. + **Enter the dev shell.** This downloads the Nix-pinned toolchain on + first run and generates `lre.bazelrc`, which the template's + `.bazelrc` already `try-import`s. ```bash nix develop ```
  • - **Start the local worker.** The template ships with a - `nativelink-lre.json5` that binds CAS, AC, scheduler, and a single - worker to `localhost:50051`. + **Point `user.bazelrc` at a NativeLink cache and executor.** The + template ships it with placeholders: ```bash - nativelink ./nativelink-lre.json5 + build --remote_cache=grpcs://TODO + build --bes_backend=grpcs://TODO + build --remote_timeout=600 + build --remote_executor=grpcs://TODO ``` + + Fill these in with either your [dev.nativelink.com](https://dev.nativelink.com) + credentials or a self-hosted cluster with a worker capable of the + platform the example needs. A plain + [local instance](/rbe/local-testing) (drop the `s` in `grpcs://`, + point at `127.0.0.1`) is enough to validate `remote_cache`, but + the C++ example's `lre-cc` platform still needs a real worker + running the matching container image — see + [Local Remote Execution](/rbe/local-remote-execution) for the + exact commands and what each one actually requires.
  • - **Point your build system at it.** The flake includes a - `.bazelrc.lre` that's pre-configured; for non-Bazel clients see - [Getting Started → Other build systems](/getting-started/other-build-systems). + **Build the example.** + + ```bash + bazel build hello-world + ```
  • The first build will be the same wall-time as a normal local build. The second one will be near-instant — that's the cache doing its job. + + The template above is the fastest way to see LRE working, but it + starts a new project. To add the same Nix-pinned toolchains to a + project you already have, see the full flake-side and Bazel-side + wiring — the `nativelink.flakeModule` import, `lre.installationScript`, + and the `local-remote-execution` Bazel module override — in + [`local-remote-execution/README.md`](https://github.com/TraceMachina/nativelink/blob/main/local-remote-execution/README.md). + + ## Why Nix, specifically? LRE needs every input to a build action to be hashable. The compiler, @@ -116,9 +140,15 @@ For background, see [What is Nix?](/faq/nix) and ## What's next +- [RBE → Local Remote Execution](/rbe/local-remote-execution) — the + Setup steps above, run for real, plus what's actually + local-vs-remote once you leave `x86_64-linux`. +- [Local cache and executor](/rbe/local-testing) — a cache and + executor to point the setup above at, no Nix required for the + server side. - [Architecture](/explanations/architecture) — the full RE-API picture LRE plugs into. - [Configuration → Basic](/configuration/basic) — the JSON5 the local worker reads. -- [RBE → Nix templates](/rbe/nix-templates) — runnable LRE - reference setups. +- [RBE → Nix templates](/rbe/nix-templates) — the `bazel` flake + template this page walks through. diff --git a/web/apps/docs/content/docs/rbe/local-remote-execution.mdx b/web/apps/docs/content/docs/rbe/local-remote-execution.mdx new file mode 100644 index 000000000..ccc194eec --- /dev/null +++ b/web/apps/docs/content/docs/rbe/local-remote-execution.mdx @@ -0,0 +1,190 @@ +--- +title: Local Remote Execution +description: The Nix-pinned toolchain workflow behind LRE, walked through and tested end to end — including exactly what runs fully local and what still needs a real worker. +--- + +[Explanations → LRE](/explanations/lre) covers what Local Remote +Execution is and why it exists. This page is the hands-on version: +the exact commands, run for real, plus two gotchas the flake alone +doesn't mention. + + + `@local-remote-execution` ships **two** toolchain families, and they + behave differently once you leave `x86_64-linux`: + + - **Rust** (`@local-remote-execution//rust/...`) has a real, natively + built toolchain for `aarch64-darwin`, `aarch64-linux`, + `x86_64-darwin`, and `x86_64-linux`. Actions using it run fully + local — no network, no container. + - **C++** (`@local-remote-execution//generated-cc/...`) — what the + `bazel` template's own example uses — is a single Linux container + config (`ghcr.io/tracemachina/nativelink-worker-lre-cc:...`). + On any host, actions using it dispatch to a real worker running + that container. On macOS this means a network round-trip even + though the toolchain is Nix-pinned — LRE gives you hermeticity + here, not offline-ness. + + Below is what actually happened running the C++ example on + aarch64-darwin. If your project is Rust, swap the target and you get + the fully-local experience the intro promises. + + +## 1. Install Nix + +Flakes enabled. The +[next-gen installer](https://github.com/NixOS/experimental-nix-installer) +is the easiest path. + +## 2. Pull the template + +```bash +mkdir my-lre-test && cd my-lre-test +nix flake init -t github:TraceMachina/nativelink#bazel +``` + +This writes a `flake.nix` that imports `nativelink.flakeModules.lre` +and pins `lre = { inherit (pkgs.lre.lre-cc.meta) Env; };`, plus the +`hello-world.cpp` example, its `BUILD.bazel`, and `platforms/BUILD.bazel`. + +## 3. Init git — this step is not optional + +```bash +git init && git add -A +``` + + + The flake module's install script checks for a `.git` directory + before generating anything. Running `nix develop` in a plain + (non-git) folder prints this and moves on without writing + `lre.bazelrc` — no error, no toolchain: + + ```text + WARNING: lre: .git not found; skipping installation. + ``` + + +## 4. Enter the dev shell + +```bash +nix develop +``` + +This fetches the Nix-pinned Clang and writes `lre.bazelrc`. On +aarch64-darwin, the generated file looked like this (paths and hashes +will differ on your machine): + +```bash +# These flags are dynamically generated by the lre flake module. +# +# PATH=/nix/store/.../llvm-binutils-wrapper-20.1.1/bin:/nix/store/.../customClang/bin:... +# CC=/nix/store/.../customClang/bin/customClang + +# Bazel-side configuration for LRE. +build --define=EXECUTOR=remote +build --extra_execution_platforms=@local-remote-execution//rust/platforms:aarch64-apple-darwin +build --extra_toolchains=@local-remote-execution//rust:rust-aarch64-darwin +build --extra_toolchains=@local-remote-execution//rust:rustfmt-aarch64-darwin +build --platforms=@local-remote-execution//rust/platforms:aarch64-apple-darwin +``` + +Notice this registers the **Rust** platform even though the flake +requested the `lre-cc` `Env` — there's no local aarch64-darwin config +under `generated-cc`, so the module falls back to the toolchain that +does have one. The template's `.bazelrc` separately adds +`--extra_execution_platforms=@//platforms:lre-cc` for the C++ example, +which is the container-based platform from the callout above. + +## 5. Point `user.bazelrc` at a real cache and executor + +The template's own generator writes `user.bazelrc` with the literal +word `TODO` as every value — this isn't a placeholder left in this +page, it's what `nix flake init` puts on disk for you to edit: + +```bash +build --remote_cache=grpcs://TODO +build --bes_backend=grpcs://TODO +build --remote_timeout=600 +build --remote_executor=grpcs://TODO +``` + +Replace all three `TODO`s with either your +[dev.nativelink.com](https://dev.nativelink.com) credentials or your +own cluster's endpoint. Concretely, filled in for a plain local +cluster (no TLS, so `grpc://` instead of `grpcs://`, and no BES +endpoint to point at) — this is the exact `user.bazelrc` used to +produce the verified output in step 4 above: + +```bash +build --remote_cache=grpc://127.0.0.1:50051 +build --remote_timeout=600 +build --remote_executor=grpc://127.0.0.1:50051 +``` + +A [dev.nativelink.com](https://dev.nativelink.com) or self-hosted +cluster looks the same shape, just with `grpcs://` and your cluster's +real hostnames in place of `127.0.0.1:50051`, plus a `bes_backend` +line if you want build results streamed there. + +If you build before editing `user.bazelrc` at all, Bazel fails fast on +the literal hostname `TODO` — here's the exact error that produces, +captured from a real run, so you recognize it as "I forgot to edit +`user.bazelrc`" rather than something else being wrong: + +```text +[8 / 10] Compiling src/hello-world.cpp; 0s remote, remote-cache +ERROR: BUILD.bazel:3:10: Compiling src/hello-world.cpp failed: + Failed to query remote execution capabilities: UNAVAILABLE: Unable to resolve host TODO +``` + +Everything before that DNS failure worked correctly — the Nix +toolchain, the Bazel platform wiring, and the `lre-cc` execution +platform selection (`0s remote, remote-cache` means Bazel committed to +executing it remotely). Once real values replace `TODO`, this step +just works. + +## 6. Build + +```bash +bazel build hello-world +``` + +With a real endpoint in `user.bazelrc` and a worker running the +`nativelink-worker-lre-cc` image available to it, this compiles +`src/hello-world.cpp` on that worker and returns the binary through +the same CAS your local Bazel reads from. Re-run and it's a cache hit. + + + You don't need a full cluster to see the cache side of this working. + [Local cache and executor](/rbe/local-testing) starts a plain + NativeLink instance on `localhost` — enough to validate + `remote_cache` against real credentials before you also need a + worker that matches `lre-cc`'s platform requirements. + + +## Troubleshooting + + + If cache hits between local and remote execution stop happening, + compare `lre.bazelrc`'s paths against the toolchain configs in the + `@local-remote-execution` module at the commit pinned in your + `MODULE.bazel` / `flake.nix`. A version drift between the two is the + most common cause. + + + + The template above bootstraps a new project. For the flake-parts + module wiring, `bazel_dep` override, and `try-import` setup to add + the same toolchains to a project you already have, see + [`local-remote-execution/README.md`](https://github.com/TraceMachina/nativelink/blob/main/local-remote-execution/README.md) + — it also covers verifying the setup against a Kubernetes cluster. + + +## What's next + +- [Explanations → LRE](/explanations/lre) — the conceptual write-up. +- [Local cache and executor](/rbe/local-testing) — validate the + protocol side without Nix or a container-capable worker. +- [Nix templates](/rbe/nix-templates) — what else ships in the + `bazel` flake template. +- [Deployment → Kubernetes](/deployment/kubernetes) — running a real + worker fleet, including `lre-cc` workers, in production. diff --git a/web/apps/docs/content/docs/rbe/local-testing.mdx b/web/apps/docs/content/docs/rbe/local-testing.mdx new file mode 100644 index 000000000..28e4dccf9 --- /dev/null +++ b/web/apps/docs/content/docs/rbe/local-testing.mdx @@ -0,0 +1,204 @@ +--- +title: Local cache and executor +description: Run a full NativeLink cache and executor on your own machine and prove Bazel's remote caching and remote execution work — no Nix, no shared cluster. +--- + +Before pointing a build at a shared cluster — or before setting up +[Local Remote Execution](/rbe/local-remote-execution) — prove the +client side works against something you fully control: a NativeLink +cache and executor running on your own machine, using whatever +toolchain is already on your `PATH`. Nothing here talks to any +external or shared service, and nothing here requires Nix. + + + [Local Remote Execution](/rbe/local-remote-execution) is a specific + NativeLink feature: Nix-pinned toolchains that make local and remote + actions hash-identical. This page is narrower and has no toolchain + opinion at all — it's the fastest way to confirm a Bazel client can + talk to a real NativeLink `Execute` and CAS implementation before + you add Nix into the mix. + + +## What you get + +One `nativelink` process, one port for the client, a private port for +the worker: + + +{`flowchart LR + subgraph client[Your machine] + B[Bazel] + subgraph nl["nativelink process"] + direction LR + L["0.0.0.0:50051\\ncas · ac · bytestream\\nexecution · capabilities"] + W["0.0.0.0:50061\\nworker_api (private)"] + WK[local worker] + end + end + B -->|remote_cache\\n+ remote_executor| L + L --- W + W --- WK`} + + +Bazel's `--remote_cache` and `--remote_executor` both point at +`127.0.0.1:50051`. `50061` only exists so the worker can register with +the scheduler — Bazel never talks to it. + +## 1. Build `nativelink` + +```bash +bazel build //:nativelink +# or: cargo build --bin nativelink +``` + +## 2. Grab the config + +[`nativelink-config/examples/local_rbe_self_test.json5`](https://github.com/TraceMachina/nativelink/blob/main/nativelink-config/examples/local_rbe_self_test.json5) +is a complete, tested cluster — CAS, AC, a `simple` scheduler, and one +`local` worker, entirely on `localhost`: + +```json5 +{ + stores: [ + { name: "CAS_MAIN_STORE", filesystem: { /* ... */ } }, + { name: "AC_MAIN_STORE", filesystem: { /* ... */ } }, + { + // The worker's fast tier and the client-facing CAS share one store, + // so anything the worker produces is immediately visible to Bazel. + name: "WORKER_FAST_SLOW_STORE", + fast_slow: { + fast: { filesystem: { /* ... */ } }, + slow: { ref_store: { name: "CAS_MAIN_STORE" } }, + }, + }, + ], + schedulers: [{ + name: "MAIN_SCHEDULER", + simple: { supported_platform_properties: { cpu_count: "minimum" /* ... */ } }, + }], + workers: [{ + local: { + worker_api_endpoint: { uri: "grpc://127.0.0.1:50061" }, + cas_fast_slow_store: "WORKER_FAST_SLOW_STORE", + upload_action_result: { ac_store: "AC_MAIN_STORE" }, + platform_properties: { cpu_count: { values: ["1"] } /* ... */ }, + }, + }], + servers: [ + { + // Cache AND execution on one client-facing port. + name: "local", + listener: { http: { socket_address: "0.0.0.0:50051" } }, + services: { + cas: [{ cas_store: "CAS_MAIN_STORE" }], + ac: [{ ac_store: "AC_MAIN_STORE" }], + bytestream: [{ cas_store: "CAS_MAIN_STORE" }], + execution: [{ cas_store: "CAS_MAIN_STORE", scheduler: "MAIN_SCHEDULER" }], + capabilities: [{ remote_execution: { scheduler: "MAIN_SCHEDULER" } }], + }, + }, + { + // Private — only the worker connects here. + name: "worker_api", + listener: { http: { socket_address: "0.0.0.0:50061" } }, + services: { worker_api: { scheduler: "MAIN_SCHEDULER" }, health: {} }, + }, + ], +} +``` + + + Every service above omits `instance_name`, so it defaults to `""` — + the same default Bazel uses when you don't pass + `--remote_instance_name`. If you add an `instance_name` on the server + side, pass the matching `--remote_instance_name` flag, or every + request will fail with `'instance_name' not configured for ''`. + + +## 3. Start it + +```bash +nativelink ./nativelink-config/examples/local_rbe_self_test.json5 +``` + +```text +INFO nativelink: Ready, listening on 0.0.0.0:50051 +INFO nativelink: Ready, listening on 0.0.0.0:50061 +INFO nativelink_worker::local_worker: Worker registered with scheduler, worker_id: 1f175824-... +``` + +## 4. Point Bazel at it + +```bash +bazel test \ + --remote_cache=grpc://127.0.0.1:50051 \ + --remote_executor=grpc://127.0.0.1:50051 \ + --remote_default_exec_properties=cpu_count=1 \ + //your:target +``` + + + It's tempting to assume `--remote_executor` alone is enough — the + executor has to read/write CAS anyway. It isn't: without an explicit + `--remote_cache`, Bazel calls `Execute` with an action digest it + never uploaded, and the server correctly rejects it — + `FAILED_PRECONDITION: Action ... is missing from CAS`. Set both + flags. Pointing both at the same local address is exactly the point: + one machine, one cache, no separate remote service. + + +This repo tests itself exactly this way — see `build:self_test` and +`build:self_execute` in +[`.bazelrc`](https://github.com/TraceMachina/nativelink/blob/main/.bazelrc), +combined as `bazel test --config=self_test --config=self_execute`. +Copy that pattern into your own `.bazelrc` once the raw flags work. + +## 5. Verify + +First run — nothing cached yet, both actions run remote: + +```text +[4 / 5] 1 / 1 tests; Testing //:dummy_test; 0s remote, remote-cache +INFO: 5 processes: 3 internal, 3 remote. +//:dummy_test PASSED in 0.4s +``` + +`bazel clean`, run again — the genrule is a cache hit; the test +re-executes remotely because `--nocache_test_results` was set: + +```text +[7 / 8] Testing //:dummy_test; 0s remote +INFO: 8 processes: 2 remote cache hit, 6 internal, 1 remote. +//:dummy_test PASSED in 0.5s +``` + +Both runs were captured against the exact config above, on macOS +(arm64) — no Linux, no containers, no Nix. + +## Troubleshooting + + + The scheduler's `supported_platform_properties` must be satisfiable + by at least one worker's `platform_properties`, or matching actions + sit in the queue indefinitely with no error. Start with just + `cpu_count: "minimum"` on the scheduler and `cpu_count: { values: ["1"] }` + (or higher) on the worker — that alone unblocks most local setups. + + + + The worker's `use_namespaces` / `use_mount_namespace` options + sandbox actions with Linux namespaces and default to `false`. Leave + them unset on macOS — setting either to `true` on an unsupported + platform makes the worker exit immediately. + + +## What's next + +- [Local Remote Execution](/rbe/local-remote-execution) — add + Nix-pinned, hermetic toolchains on top of this same idea. +- [Classic RBE examples](/rbe/examples) — the same three patterns + (cache-only, full RE, hybrid) once you're pointed at a shared cluster. +- [Nix templates](/rbe/nix-templates) — the `bazel` flake template, + for wiring a whole new project instead of the existing NativeLink repo. +- [Configuration → Introduction](/configuration/intro) — what every + field in the config above actually does. diff --git a/web/apps/docs/content/docs/rbe/meta.json b/web/apps/docs/content/docs/rbe/meta.json index 7b27cf714..0a9b612e5 100644 --- a/web/apps/docs/content/docs/rbe/meta.json +++ b/web/apps/docs/content/docs/rbe/meta.json @@ -1,5 +1,7 @@ { "pages": [ + "local-testing", + "local-remote-execution", "examples", "nix-templates" ], diff --git a/web/apps/docs/content/docs/rbe/nix-templates.mdx b/web/apps/docs/content/docs/rbe/nix-templates.mdx index 6235bd9f0..0e2dcf8e5 100644 --- a/web/apps/docs/content/docs/rbe/nix-templates.mdx +++ b/web/apps/docs/content/docs/rbe/nix-templates.mdx @@ -1,12 +1,12 @@ --- title: Nix templates -description: Reproducible NativeLink testbeds in a single flake init command. +description: The Nix flake template NativeLink ships — Local Remote Execution with Bazel in one flake init command. --- -NativeLink ships Nix flake templates that spin up a complete -remote-execution environment in one command. Useful for -experimenting with configuration changes, demoing the system, or -debugging a regression on a known-good baseline. +NativeLink ships a Nix flake template that wires a Bazel project to +Local Remote Execution in one command. Useful for experimenting with +LRE, demoing the system, or debugging a regression on a known-good +baseline. ## Prerequisites @@ -21,102 +21,101 @@ debugging a regression on a known-good baseline. nix flake show github:TraceMachina/nativelink ``` -The templates we ship and maintain: + + Templates get added and removed between releases. Run the command + above rather than trusting a hardcoded list — including the one + below, which only reflects what's shipping as of this page's last + update. + -| Template | What it gives you | -| ------------- | ---------------------------------------------------------- | -| `bazel` | Bazel monorepo wired to a local NativeLink cluster. | -| `cargo` | Pure Cargo project with sccache pointing at NativeLink. | -| `lre` | Local Remote Execution — hermetic builds, no network. | -| `kubernetes` | `kind` cluster running the NativeLink Helm chart. | +| Template | What it gives you | +| --- | --- | +| `bazel` | A Bazel `cc_binary` project pre-wired for [Local Remote Execution](/explanations/lre): Nix-pinned toolchain, LRE Bazel module, and a `user.bazelrc` you point at a cache and executor. | ## The Bazel template -The fastest path to a working RBE setup: - ```bash mkdir my-rbe-test && cd my-rbe-test nix flake init -t github:TraceMachina/nativelink#bazel nix develop ``` -Inside the dev shell you have: - -- A pinned Bazel + Bazelisk. -- A NativeLink server binary. -- A `.bazelrc.local` already pointed at `localhost:50051`. - -Start the NativeLink server in one terminal: - -```bash -nativelink ./config/nativelink.json5 -``` - -Build the included sample target in another: - -```bash -bazel build //hello -``` - -Re-run; everything should hit the cache. - -## The LRE template - -For [Local Remote Execution](/explanations/lre): +`nix develop` downloads the Nix-pinned toolchain and generates +`lre.bazelrc`, which the template's `.bazelrc` already `try-import`s. +It does **not** bundle a NativeLink server — you point it at one you +already have: ```bash -mkdir my-lre-test && cd my-lre-test -nix flake init -t github:TraceMachina/nativelink#lre -nix develop +# In user.bazelrc +build --remote_cache=grpcs://TODO +build --bes_backend=grpcs://TODO +build --remote_timeout=600 +build --remote_executor=grpcs://TODO ``` -The dev shell provides a fully-hermetic Nix-pinned toolchain. -NativeLink runs on `localhost`; Bazel is pre-configured to use it. +Three ways to fill in `TODO` — [Local Remote Execution → Point +`user.bazelrc` at a real cache and executor](/rbe/local-remote-execution) +has a worked, filled-in example of the file below, whichever option +you pick: + +- **[dev.nativelink.com](https://dev.nativelink.com)** — paste your + cloud credentials, use `grpcs://`. +- **A self-hosted cluster** — your own endpoint, `grpcs://` if it + terminates TLS. +- **Nothing but your own machine** — drop the `s`, point both + `remote_cache` and `remote_executor` at `grpc://127.0.0.1:50051`. + [Local cache and executor](/rbe/local-testing) gets you a + from-scratch, credential-free instance in one command — enough to + validate `remote_cache`, though the included C++ example's `lre-cc` + platform still needs a worker running the matching container image + to actually execute (see [Local Remote Execution](/rbe/local-remote-execution) + for what that involves). + +Then build the included example: ```bash -nativelink ./config/lre.json5 -bazel build //hello +bazel build hello-world ``` -The first build of any target is the cost of compilation. Every -subsequent build is the cost of one network round-trip to -`localhost`. +Re-run; the cached actions should skip straight to a cache hit. -## The Kubernetes template +## Not shipped as flake templates today -For prototyping a real deployment without leaving your laptop: - -```bash -mkdir my-k8s-test && cd my-k8s-test -nix flake init -t github:TraceMachina/nativelink#kubernetes -nix develop -``` - -The flake includes `kind`, `kubectl`, `helm`, and the NativeLink -chart. Bring up a local cluster: - -```bash -kind create cluster -helm install nativelink ./chart -kubectl wait --for=condition=Ready pods --all -``` +Earlier drafts of this page also described `cargo`, `lre`, and +`kubernetes` templates. None exist in `flake.nix` right now — running +`nix flake init -t github:TraceMachina/nativelink#lre` (or `#cargo`, +`#kubernetes`) fails with `does not provide attribute 'templates.X'`. +If you followed a link here looking for one of those: -You now have CAS + scheduler + workers running in a local cluster -on your laptop. Port-forward `:50051` and point Bazel at it. +- **A local Bazel cluster with no Nix at all** — [Local cache and + executor](/rbe/local-testing) starts CAS, AC, a scheduler, and a + worker in one `nativelink` process. +- **A real Kubernetes deployment** — [Deployment → Kubernetes](/deployment/kubernetes) + covers the Helm chart directly; `kind` + that chart gets you the + same result a `kubernetes` template would have. +- **Cargo / sccache** — not currently packaged as a template; the + [`bazel` template](#the-bazel-template) above is the maintained path. ## Customising -Every template is a flake — fork and edit. The most common +The template is a flake — fork and edit. The most common modifications: -- Swap the `nativelink.json5` for a config matching your real - environment. -- Add custom worker platform properties. -- Pin to a specific NativeLink release (`inputs.nativelink.url`). +- Point `user.bazelrc` at your real cluster instead of a throwaway one. +- Add custom worker platform properties in `platforms/BUILD.bazel`. +- Pin to a specific NativeLink commit (`inputs.nativelink.url` in + `flake.nix`, kept in sync with the `local-remote-execution` module + override in `MODULE.bazel`). ## What's next -- [Architecture](/explanations/architecture) — the data flow - these templates exercise. -- [Configuration → Basic](/configuration/basic) — the JSON5 - the templates ship. +- [Local Remote Execution](/rbe/local-remote-execution) — this same + template, walked through step by step and tested for real. +- [Local cache and executor](/rbe/local-testing) — the fastest way to + get something running on `127.0.0.1` to point this template at. +- [Explanations → LRE](/explanations/lre) — what the toolchain pinning + this template sets up actually buys you. +- [Architecture](/explanations/architecture) — the data flow this + template exercises. +- [Configuration → Basic](/configuration/basic) — the JSON5 shape a + server you point this at should speak. diff --git a/web/apps/docs/content/docs/reference/nativelink-config/main.mdx b/web/apps/docs/content/docs/reference/nativelink-config/main.mdx index d8c56476f..02431ba98 100644 --- a/web/apps/docs/content/docs/reference/nativelink-config/main.mdx +++ b/web/apps/docs/content/docs/reference/nativelink-config/main.mdx @@ -5,7 +5,7 @@ full: true --- {/* AUTOGENERATED — do not edit by hand. - Source: nativelink-config @ main (cfb8141e) + Source: nativelink-config @ main (ce3a919e) Regenerate from web/: bun --filter @nativelink/docs gen:config-reference */} @@ -180,6 +180,55 @@ It supports the following backends: } ``` +5. **Cloudflare R2:** + R2 store uses Cloudflare's R2 service as a backend. R2 speaks the + S3 API, so this is a thin wrapper that derives the account-scoped + endpoint (`https://{account_id}.r2.cloudflarestorage.com`) for you. + + **Example JSON5 config:** + ```json5 + "experimental_cloud_object_store": { + "provider": "r2", + "account_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", + "bucket": "nativelink-cas", + "key_prefix": "test-prefix/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5 + }, + "multipart_max_concurrent_uploads": 10 + } + ``` + +6. **Oracle Cloud Infrastructure (OCI) Object Storage:** + OCI store uses Oracle Cloud Infrastructure's S3-compatible Object + Storage API. The path-style endpoint is derived from your Object + Storage `namespace` and `region` as + `https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com`. + Authenticate with a Customer Secret Key (Access Key/Secret Key pair + created under User Settings -> Customer secret keys in the OCI + console); the secret cannot be retrieved after generation, so read + it from an env var via shellexpand. + + **Example JSON5 config:** + ```json5 + "experimental_cloud_object_store": { + "provider": "oci", + "namespace": "your-object-storage-namespace", + "region": "us-phoenix-1", + "bucket": "nativelink-cas", + "access_key_id": "${OCI_ACCESS_KEY_ID}", + "secret_access_key": "${OCI_SECRET_ACCESS_KEY}", + "key_prefix": "test-prefix/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5 + } + } + ``` + **Type:** [ExperimentalCloudObjectSpec](#experimentalcloudobjectspec) ### `ontap_s3_existence_cache` @@ -859,8 +908,8 @@ Configuration for `ExperimentalMongoDB` store. | `worker_api_endpoint` | [EndpointConfig](#endpointconfig) | Yes | — | Endpoint which the worker will connect to the scheduler's `WorkerApiService`. | | `max_action_timeout_s` | integer (uint) | — | 20 minutes | The maximum time an action is allowed to run. If a task requests for a timeout longer than this time limit, the task will be rejected. Value in seconds. | | `max_upload_timeout_s` | integer (uint) | — | 10 minutes | Maximum time allowed for uploading action results to CAS after execution completes. If upload takes longer than this, the action fails with `DeadlineExceeded` and may be retried by the scheduler. Value in seconds. | -| `max_cleanup_wait_s` | integer (uint) | — | 30 seconds | Maximum time to wait for action directory cleanup before timing out. When an action completes, the worker waits for the directory to be cleaned up before reusing it. If cleanup takes longer than this, the action fails with `DeadlineExceeded`. Value in seconds. | -| `max_cleanup_backoff_ms` | integer (uint) | — | 500 milliseconds | Maximum backoff duration for exponential backoff when waiting for cleanup. When waiting for a previous operation's cleanup to complete, the worker uses exponential backoff starting from a small value and increasing up to this maximum. Value in milliseconds. | +| `max_cleanup_wait_s` | integer (uint) | — | 30 seconds | Maximum time to wait for action directory cleanup before timing out. Value in seconds. | +| `max_cleanup_backoff_ms` | integer (uint) | — | 500 milliseconds | Maximum backoff duration for exponential backoff when waiting for cleanup. Value in milliseconds. | | `max_inflight_tasks` | integer (uint64) | — | 0 (infinite tasks) | Maximum number of inflight tasks this worker can cope with. | | `timeout_handled_externally` | boolean | — | false (`NativeLink` fully handles timeouts) | If timeout is handled in `entrypoint` or another wrapper script. If set to true `NativeLink` will not honor the timeout the action requested and instead will always force kill the action after `max_action_timeout` has been reached. If this is set to false, the smaller value of the action's timeout and `max_action_timeout` will be used to which `NativeLink` will kill the action. | | `entrypoint` | string | — | {Use the command from the job request} | The command to execute on every execution request. This will be parsed as a command + arguments (not shell). Example: "run.sh" and a job with command: "sleep 5" will result in a command like: "run.sh sleep 5". | @@ -1114,6 +1163,55 @@ It supports the following backends: } ``` +5. **Cloudflare R2:** + R2 store uses Cloudflare's R2 service as a backend. R2 speaks the + S3 API, so this is a thin wrapper that derives the account-scoped + endpoint (`https://{account_id}.r2.cloudflarestorage.com`) for you. + + **Example JSON5 config:** + ```json5 + "experimental_cloud_object_store": { + "provider": "r2", + "account_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", + "bucket": "nativelink-cas", + "key_prefix": "test-prefix/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5 + }, + "multipart_max_concurrent_uploads": 10 + } + ``` + +6. **Oracle Cloud Infrastructure (OCI) Object Storage:** + OCI store uses Oracle Cloud Infrastructure's S3-compatible Object + Storage API. The path-style endpoint is derived from your Object + Storage `namespace` and `region` as + `https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com`. + Authenticate with a Customer Secret Key (Access Key/Secret Key pair + created under User Settings -> Customer secret keys in the OCI + console); the secret cannot be retrieved after generation, so read + it from an env var via shellexpand. + + **Example JSON5 config:** + ```json5 + "experimental_cloud_object_store": { + "provider": "oci", + "namespace": "your-object-storage-namespace", + "region": "us-phoenix-1", + "bucket": "nativelink-cas", + "access_key_id": "${OCI_ACCESS_KEY_ID}", + "secret_access_key": "${OCI_SECRET_ACCESS_KEY}", + "key_prefix": "test-prefix/", + "retry": { + "max_retries": 6, + "delay": 0.3, + "jitter": 0.5 + } + } + ``` + **Type:** [ExperimentalCloudObjectSpec](#experimentalcloudobjectspec) ### `ontap_s3_existence_cache` diff --git a/web/apps/docs/content/docs/reference/nativelink-config/meta.json b/web/apps/docs/content/docs/reference/nativelink-config/meta.json index 35333832f..22e3bb1a6 100644 --- a/web/apps/docs/content/docs/reference/nativelink-config/meta.json +++ b/web/apps/docs/content/docs/reference/nativelink-config/meta.json @@ -1,6 +1,7 @@ { "pages": [ - "index" + "index", + "store-overview" ], "title": "NativeLink config" } diff --git a/web/apps/docs/content/docs/reference/nativelink-config/store-overview.mdx b/web/apps/docs/content/docs/reference/nativelink-config/store-overview.mdx new file mode 100644 index 000000000..ca4b343fa --- /dev/null +++ b/web/apps/docs/content/docs/reference/nativelink-config/store-overview.mdx @@ -0,0 +1,171 @@ +--- +title: Store overview +description: The mental model behind every store in the configuration reference — what holds data, what wraps another store, and how they compose. +--- + +The [full reference](/reference/nativelink-config) lists every field on +every store. This page is the map you need before that list is useful: +what a store actually is, the two families every store falls into, and +the compositions people actually run in production. + +## What a store is + +A store is a named, pluggable backend for CAS or AC data. You declare +each one once in the top-level `stores` array, then reference it by +name from `servers`, `workers`, or from inside another store: + +```json5 +{ + stores: [ + { name: "CAS_MAIN_STORE", filesystem: { /* ... */ } }, + ], + servers: [{ + services: { + cas: [{ instance_name: "main", cas_store: "CAS_MAIN_STORE" }], + }, + }], +} +``` + +The store's *type* is whichever key you put inside its object — +`filesystem`, `memory`, `compression`, `fast_slow`, and so on. NativeLink +picks the implementation from that key; everything else in the object is +that implementation's config. + +## Two families + +Every store type is either **terminal** (it actually holds or fetches +bytes) or a **wrapper** (it adds behavior in front of another store, +which it embeds as a nested `StoreSpec`). Wrappers nest arbitrarily +deep — a `verify` can wrap a `compression`, which wraps a `fast_slow`, +which wraps two more terminal stores: + + +{`flowchart LR + V["verify\\n(hash + size check)"] --> C["compression\\n(lz4)"] + C --> F["fast_slow"] + F -->|fast| M[("memory")] + F -->|slow| D[("filesystem")]`} + + +Reads and writes flow through every wrapper in order. Nothing about the +client changes — it still speaks plain CAS/AC RPCs against whatever +store name the server exposes. + +### Terminal stores — where bytes actually live + +| Store key | Holds data in | Notes | +| --- | --- | --- | +| `memory` | An in-process hash map | Fastest, gone on restart. Good for AC tiers and dev. | +| `filesystem` | Local disk | Survives restarts; scans and rebuilds its index on startup. | +| `experimental_cloud_object_store` | A cloud object bucket | One store type, six `provider`s: `aws` (S3), `gcs`, `azure`, `ontap` (NetApp ONTAP S3), `r2` (Cloudflare), `oci` (Oracle Cloud Infrastructure). All speak an S3-compatible API except `gcs` and `azure`. | +| `redis_store` | Any Redis-API-compatible service | Pairs well with `size_partitioning` — most Redis services cap uploads around 256–512 MB. | +| `experimental_mongo` | MongoDB | Supports CAS and scheduler data, with optional change streams for scheduler subscriptions. | +| `grpc` | Another NativeLink-compatible gRPC endpoint | Proxies calls upstream. Useful for a regional cache in front of a central cluster. | +| `ref_store` | Nothing itself — points at another store by name | Lets two composition trees share one underlying store instance (see below). | +| `noop` | Nothing | Reads 404. Writes vanish. Used to explicitly discard a partition of data. | + +### Wrapper stores — behavior layered on a backend + +| Store key | Wraps | What it adds | +| --- | --- | --- | +| `compression` | one store | LZ4-compresses on write, decompresses on read. | +| `dedup` | two stores (`index_store` + `content_store`) | Rolling-hash chunking so only changed slices upload. | +| `fast_slow` | two stores (`fast` + `slow`) | Reads try `fast` first, fall back to `slow`, and backfill `fast`. Writes mirror to both. | +| `shard` | N stores | Routes by digest hash. The standard shape for scaling CAS past one backend. | +| `size_partitioning` | two stores (`lower_store` + `upper_store`) | Routes by blob size instead of hash. CAS-only — see the gotcha below. | +| `verify` | one store | Rejects uploads that fail a hash and/or size check before they ever reach the backend. | +| `existence_cache` | one store | Caches `has()` results. CAS-only. | +| `completeness_checking` | one store + a CAS store | Confirms an `ActionResult`'s output digests exist in CAS before returning it. AC-only. | +| `cache_metrics` | one store | Emits OpenTelemetry cache-hit/miss metrics for the wrapped store. Opt-in — stores you don't wrap pay nothing extra. | +| `ontap_s3_existence_cache` | (built-in ONTAP S3 backend) | Purpose-built existence cache for ONTAP S3 specifically, with disk-persisted sync instead of a generic wrapped backend. | + + + - Put `dedup` *inside* `compression` (compression wraps dedup's output), + never the other way — `compression` as `dedup`'s `content_store` negates + dedup's gains, since every chunk becomes a differently-compressed blob. + - `fast_slow` never checks whether an object in `fast` also exists in + `slow`. If you need a durability guarantee — e.g. remote execution + artifacts that must survive a `fast`-tier wipe — write-through both + tiers deliberately rather than assuming it. + - `existence_cache` and `size_partitioning` are CAS-only; `completeness_checking` + is AC-only. Using them on the other store type produces confusing + correctness bugs, not a config error. + + +## Choosing a terminal backend + +| Backend | Durability | Shared across instances | Typical role | +| --- | --- | --- | --- | +| `memory` | None (restart wipes it) | No | Fast tier in a `fast_slow`, or a whole AC store for a short-lived CI runner. | +| `filesystem` | Survives restarts | No (single node) | Single-node dev/CI cache, or the fast/local tier in front of shared storage. | +| `experimental_cloud_object_store` | Durable, provider-managed | Yes | The shared, multi-node backing store in most production clusters. | +| `redis_store` | As durable as your Redis deployment | Yes | Low-latency shared tier, usually fronted by `size_partitioning` to keep large blobs out of it. | +| `experimental_mongo` | Durable | Yes | Alternative shared backend when you already operate MongoDB and want scheduler-state change streams. | + +## Common compositions + +**Single-node dev cache** — no wrapping at all. See +[Configuration → Basic](/configuration/basic). + +**Validated, compressed, tiered CAS** — the shape most self-hosted +production clusters converge on: + +```json5 +{ + name: "CAS_MAIN_STORE", + verify: { + verify_size: true, + verify_hash: true, + backend: { + compression: { + compression_algorithm: { lz4: {} }, + backend: { + fast_slow: { + fast: { memory: { eviction_policy: { max_bytes: "2gb" } } }, + slow: { + experimental_cloud_object_store: { + provider: "aws", + region: "us-east-1", + bucket: "nativelink-cas", + }, + }, + }, + }, + }, + }, + }, +} +``` + +**Sharing one store instance across two trees** — use `ref_store` +instead of declaring the same backend twice: + +```json5 +{ + stores: [ + { + name: "FS_CONTENT_STORE", + filesystem: { content_path: "/var/lib/nativelink/cas", temp_path: "/var/lib/nativelink/tmp" }, + }, + { + // The worker's fast/local tier and the AC's fast tier now share + // one filesystem store instead of each scanning their own copy. + name: "AC_MAIN_STORE", + fast_slow: { + fast: { ref_store: { name: "FS_CONTENT_STORE" } }, + slow: { noop: {} }, + }, + }, + ], +} +``` + +## What's next + +- [Configuration reference](/reference/nativelink-config) — every + field, default, and JSON5 example for every store type above. +- [Configuration → Introduction](/configuration/intro) — how stores + fit alongside servers, schedulers, and workers. +- [Configuration → Production](/configuration/production) — sharded, + multi-region store topologies end to end. diff --git a/web/apps/docs/scripts/gen-config-reference.mjs b/web/apps/docs/scripts/gen-config-reference.mjs index fcf137f7d..051cc4859 100644 --- a/web/apps/docs/scripts/gen-config-reference.mjs +++ b/web/apps/docs/scripts/gen-config-reference.mjs @@ -252,12 +252,17 @@ export const CONFIG_VERSIONS: ConfigVersion[] = ${JSON.stringify(entries, null, writeFileSync(manifestFile, body); } +// Hand-written pages that live alongside the autogenerated reference in this +// folder. Keep these out of `resolveVersions()`/`generateOne()` — they're not +// versioned schema dumps — but they must survive `writeMeta()` below. +const HAND_WRITTEN_PAGES = ["store-overview"]; + function writeMeta() { - // Only the canonical page appears in the sidebar; other versions are reached - // through the in-page version switcher. + // The canonical page plus any hand-written companion pages appear in the + // sidebar; other versions are reached through the in-page version switcher. writeFileSync( join(contentDir, "meta.json"), - `${JSON.stringify({ pages: ["index"], title: "NativeLink config" }, null, 2)}\n`, + `${JSON.stringify({ pages: ["index", ...HAND_WRITTEN_PAGES], title: "NativeLink config" }, null, 2)}\n`, ); } From 503056f16ddb13d5aabc76cc54b1af2032f4c384 Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Fri, 3 Jul 2026 16:25:06 -0700 Subject: [PATCH 013/144] Regenerate Changelog on New Release (#2500) * Regenerate Changelog on New Release * Fix incomplete multi-character sanitization in gen-changelog CodeQL flagged the single-pass strip of spans: crafted input like - y --> reconstructs ", + "", + "", +]); + +const { text: raw, origin } = await readChangelog(); + +// Keep everything from the first release heading on; the git-cliff header +// (comment markers, title, "All notable changes..." line) is replaced by the +// intro below. +const firstHeading = raw.search(/^## /m); +if (firstHeading === -1) { + throw new Error(`no "## " release heading found in ${origin}`); +} + +let inFence = false; +const body = raw + .slice(firstHeading) + .split("\n") + .map((line) => { + if (/^\s{0,3}(```|~~~)/.test(line)) { + inFence = !inFence; + return line; + } + if (inFence) { + return line; + } + return GIT_CLIFF_MARKER_LINES.has(line.trim()) ? null : escapeLine(line); + }) + .filter((line) => line !== null) + .join("\n") + .trimEnd(); + +const page = `--- +title: Changelog +description: Notable changes per release. Latest first. +--- + + + + +This page mirrors +[\`CHANGELOG.md\`](https://github.com/TraceMachina/nativelink/blob/main/CHANGELOG.md), +the canonical changelog maintained with [git-cliff](https://git-cliff.org) +as part of each release. + +${body} +`; + +mkdirSync(dirname(targetFile), { recursive: true }); +writeFileSync(targetFile, page); +console.log(`gen-changelog: wrote ${targetFile}`); diff --git a/web/turbo.json b/web/turbo.json index bb964c964..5ed29952d 100644 --- a/web/turbo.json +++ b/web/turbo.json @@ -1,6 +1,23 @@ { "$schema": "https://turbo.build/schema.json", "tasks": { + "@nativelink/docs#build": { + "dependsOn": [ + "^build" + ], + "env": [ + "VERCEL_GIT_COMMIT_SHA" + ], + "inputs": [ + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/../CHANGELOG.md" + ], + "outputs": [ + ".next/**", + "!.next/cache/**", + "dist/**" + ] + }, "build": { "dependsOn": [ "^build" From 6dca947a135bea52585ebaa4a627510656e02b62 Mon Sep 17 00:00:00 2001 From: corcillo Date: Fri, 3 Jul 2026 19:42:03 -0700 Subject: [PATCH 014/144] [web] restore blog posts deleted by the site redesign (#2501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * web: restore the 10 blog posts deleted by the site redesign PR #2371 replaced the old Astro site (web/platform) with the new Next.js app and deleted all blog content with it — the 10 real posts under web/platform/src/content/posts plus the /resources/blog routes. The new /resources page shipped with placeholder cards linking to "#". Restore the posts verbatim from 2c549617^ into web/apps/web/content/posts and rebuild the routes in the new app: - /resources/blog — index of all posts, newest first - /resources/blog/ — post pages, prerendered via generateStaticParams, markdown rendered with marked into the existing Prose component Slugs come from each post's original frontmatter, so the old /resources/blog/ URLs resolve again. marked@16.4.2 was already in the lockfile (via the docs app), so no new transitive deps. CaseStudy_Samsung is deliberately NOT restored — it was removed separately (#1464, #1576) pending approval, before the redesign. No changes to any existing page. * web: point the resources page's "All posts" link at the restored blog The Announcements header's "All posts →" link has been a dead "#" href since the redesign. Point it at /resources/blog so the restored posts are reachable by click. Only href change — no copy or layout edits. * web: fix remaining dead links found by full link audit Audited every href on /resources, /resources/blog, and all 10 post pages (internal routes + external URLs): - The "Hermetic toolchain creation with LRE & Nix" talk card on /resources was a dead "#" href — point it at the YouTube recording of that talk (uokjTev8myk, the same video embedded beside it), opening in a new tab like the featured-post card. - The LastMile AI case study linked https://mcp-agent.com/, whose domain no longer resolves (NXDOMAIN). Point it at the project's GitHub repo (github.com/lastmile-ai/mcp-agent) — the one deliberate deviation from the verbatim restore. Everything else checks out: all internal routes 200, all external links resolve. (/docs 404s only when running the web app standalone — it's the separate docs app, routed together in production.) * web: resolve CodeQL alerts on the blog restore - js/incomplete-multi-character-sanitization (lib/posts.ts): strip HTML tags in deriveExcerpt to a fixpoint instead of a single pass, so overlapping fragments can't reassemble into a tag. The excerpt is rendered as JSX text (React-escaped) either way; this makes the sanitizer complete rather than cosmetic. - js/stored-xss (resources/blog/page.tsx): encode the file-derived slug with encodeURIComponent in the card href. Behavior-neutral — every restored slug is already URL-safe — but closes the tainted path CodeQL tracks from file content into an href. * web: group the blog index into case studies / announcements / others Review feedback on #2501 asked for one section per content type. The grouping comes from the posts' original frontmatter tags (case-studies, announcements; everything else lands in "More from the blog"), so no content changes — same ten cards, now under three eyebrow-headed sections in that order, newest first within each. A section with no posts renders nothing. * web: promote the resources "All posts" link to a visible button The blog entry point was a small mono text link, hidden entirely on mobile (hidden md:inline-flex). Use the design system's outline Button instead, visible at every viewport, so the restored blog is discoverable from /resources on phones too. * web: inline the full blog listing on /resources, drop the video embed Move the entire blog listing (case studies / announcements / more) onto the resources page under the "From the team" header, so posts are readable without leaving /resources. The card/section markup moves to a shared app/resources/post-sections.tsx used by both this page and /resources/blog, which stays as a standalone index for direct links and the posts' back-links. The hermetic-toolchains talk is no longer embedded as a YouTube player: it becomes a regular card at the end of "More from the blog", linking out to the recording. Its card metadata now says "BazelCon 2024" — the talk's actual venue — instead of the placeholder date the redesign had invented. The "All posts" link is gone since the posts now live on the page itself. * web: rename the resources blog section header to just "Blog" Drop the Blog eyebrow above "From the team" and use "Blog" as the section heading itself. * web: "From the team" header, rename last post group to "Blog" Restore "From the team" as the listing's heading (no eyebrow) and title the final group "Blog" instead of "More from the blog", on both /resources and the standalone /resources/blog index. --- .../web/app/resources/blog/[slug]/page.tsx | 93 +++++++ web/apps/web/app/resources/blog/page.tsx | 49 ++++ web/apps/web/app/resources/page.tsx | 142 ++++------ web/apps/web/app/resources/post-sections.tsx | 85 ++++++ .../web/content/posts/Accelerating_CMake.mdx | 256 ++++++++++++++++++ .../posts/Adding_Support_for_Trust_Roots.mdx | 143 ++++++++++ .../content/posts/Announcement_NativeLink.mdx | 36 +++ .../Announcement_TraceMachina_Seedfunding.mdx | 113 ++++++++ web/apps/web/content/posts/CaseStudy_CIQ.mdx | 121 +++++++++ .../web/content/posts/CaseStudy_Fortune50.mdx | 28 ++ .../content/posts/CaseStudy_LastMileAI.mdx | 38 +++ .../posts/CaseStudy_ThirdwaveAutomation.mdx | 84 ++++++ .../web/content/posts/Finetune_LLM_On_CPU.mdx | 97 +++++++ .../posts/NativeLink_for_Semiconductors.md | 39 +++ web/apps/web/lib/posts.ts | 104 +++++++ web/apps/web/package.json | 1 + web/bun.lock | 1 + 17 files changed, 1342 insertions(+), 88 deletions(-) create mode 100644 web/apps/web/app/resources/blog/[slug]/page.tsx create mode 100644 web/apps/web/app/resources/blog/page.tsx create mode 100644 web/apps/web/app/resources/post-sections.tsx create mode 100644 web/apps/web/content/posts/Accelerating_CMake.mdx create mode 100644 web/apps/web/content/posts/Adding_Support_for_Trust_Roots.mdx create mode 100644 web/apps/web/content/posts/Announcement_NativeLink.mdx create mode 100644 web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx create mode 100644 web/apps/web/content/posts/CaseStudy_CIQ.mdx create mode 100644 web/apps/web/content/posts/CaseStudy_Fortune50.mdx create mode 100644 web/apps/web/content/posts/CaseStudy_LastMileAI.mdx create mode 100644 web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx create mode 100644 web/apps/web/content/posts/Finetune_LLM_On_CPU.mdx create mode 100644 web/apps/web/content/posts/NativeLink_for_Semiconductors.md create mode 100644 web/apps/web/lib/posts.ts diff --git a/web/apps/web/app/resources/blog/[slug]/page.tsx b/web/apps/web/app/resources/blog/[slug]/page.tsx new file mode 100644 index 000000000..508da8a3b --- /dev/null +++ b/web/apps/web/app/resources/blog/[slug]/page.tsx @@ -0,0 +1,93 @@ +import { Badge, Eyebrow, Prose, Section } from "@nativelink/ui"; +import { marked } from "marked"; +import { notFound } from "next/navigation"; +import { formatPostDate, getAllPosts, getPost } from "../../../../lib/posts"; + +export function generateStaticParams() { + return getAllPosts().map((post) => ({ slug: post.slug })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const post = getPost((await params).slug); + if (!post) return {}; + return { + title: post.title, + description: post.excerpt, + openGraph: { + title: post.title, + description: post.excerpt, + type: "article", + ...(post.image ? { images: [{ url: post.image }] } : {}), + }, + }; +} + +export default async function BlogPostPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const post = getPost((await params).slug); + if (!post) notFound(); + + const html = marked.parse(post.body, { async: false }); + + return ( + <> +
    +
    +
    +
    + + ← All posts + +
    + {post.tags.map((tag) => ( + + {tag} + + ))} +
    +

    + {post.title} +

    +

    + {formatPostDate(post.pubDate)} + {post.readTime ? ` · ${post.readTime}` : null} +

    +
    +
    +
    + +
    +
    + +
    + + NativeLink Blog + +

    + + ← Back to all posts + +

    +
    +
    +
    + + ); +} diff --git a/web/apps/web/app/resources/blog/page.tsx b/web/apps/web/app/resources/blog/page.tsx new file mode 100644 index 000000000..708d4b202 --- /dev/null +++ b/web/apps/web/app/resources/blog/page.tsx @@ -0,0 +1,49 @@ +import { Eyebrow, Reveal, Section } from "@nativelink/ui"; +import { getAllPosts } from "../../../lib/posts"; +import { PostSection, postToCard } from "../post-sections"; + +export const metadata = { title: "Blog" }; + +export default function BlogIndexPage() { + const posts = getAllPosts(); + const caseStudies = posts.filter((p) => p.tags.includes("case-studies")); + const announcements = posts.filter( + (p) => p.tags.includes("announcements") && !p.tags.includes("case-studies"), + ); + const others = posts.filter( + (p) => !p.tags.includes("case-studies") && !p.tags.includes("announcements"), + ); + + return ( + <> +
    +
    +
    + +
    + Blog +

    + Writing from the team +

    +

    + Tutorials, case studies, and announcements from the people building NativeLink. +

    +
    +
    +
    +
    + + + + + + ); +} diff --git a/web/apps/web/app/resources/page.tsx b/web/apps/web/app/resources/page.tsx index 80e404bf7..e27a62558 100644 --- a/web/apps/web/app/resources/page.tsx +++ b/web/apps/web/app/resources/page.tsx @@ -1,13 +1,6 @@ -import { - Badge, - Eyebrow, - Reveal, - Section, - YouTubeEmbed, - cn, -} from "@nativelink/ui"; - -// (Badge is used in featured + announcement cards below.) +import { Badge, Eyebrow, Reveal, Section } from "@nativelink/ui"; +import { getAllPosts } from "../../lib/posts"; +import { type PostCardData, PostSection, postToCard } from "./post-sections"; export const metadata = { title: "Resources" }; @@ -21,18 +14,30 @@ const featuredPost = { href: "https://reidkleckner.dev/posts/llvm-recc-nativelink/", }; -const announcements = [ - { - tag: "Talk", - title: "Hermetic toolchain creation with LRE & Nix", - excerpt: "Aaron Mondal walks through Local Remote Execution — running fully hermetic Bazel builds on your own laptop, no Docker required.", - date: "April 18, 2026", - readingTime: "32 min video", - accent: "default", - }, -]; +// The BazelCon talk lives among the post cards rather than as an +// embedded video so the page stays light and the blog content leads. +const talkCard: PostCardData = { + key: "talk-hermetic-toolchains", + href: "https://www.youtube.com/watch?v=uokjTev8myk", + external: true, + tags: ["talk"], + meta: "BazelCon 2024 · 32 min video", + title: "Hermetic toolchain creation with LRE & Nix", + excerpt: + "Aaron Mondal walks through Local Remote Execution — running fully hermetic Bazel builds on your own laptop, no Docker required.", + cta: "Watch talk", +}; export default function ResourcesPage() { + const posts = getAllPosts(); + const caseStudies = posts.filter((p) => p.tags.includes("case-studies")); + const announcements = posts.filter( + (p) => p.tags.includes("announcements") && !p.tags.includes("case-studies"), + ); + const others = posts.filter( + (p) => !p.tags.includes("case-studies") && !p.tags.includes("announcements"), + ); + return ( <> {/* HERO */} @@ -50,8 +55,8 @@ export default function ResourcesPage() { .

    - Case studies, conference talks, and write-ups from the team building - NativeLink — plus highlights from the broader community. + Case studies, conference talks, and write-ups from the team building NativeLink — + plus highlights from the broader community.

    @@ -85,7 +90,10 @@ export default function ResourcesPage() {
    Read the write-up{" "} -
    @@ -120,74 +128,32 @@ export default function ResourcesPage() { - {/* ANNOUNCEMENTS + VIDEO */} -
    - -
    -
    - Announcements -

    - From the team -

    -
    - - All posts → - -
    -
    - -
    + {/* FROM THE TEAM — full blog listing */} +
    +
    -
    - -
    -

    - - Featured talk ·{" "} - - Hermetic Toolchain Creation with Local Remote Execution & Nix -

    -

    - Aaron Mondal, Trace Machina — 32 min -

    +

    + From the team +

    +
    - -
    -
    - + + + + ); } diff --git a/web/apps/web/app/resources/post-sections.tsx b/web/apps/web/app/resources/post-sections.tsx new file mode 100644 index 000000000..ef087b289 --- /dev/null +++ b/web/apps/web/app/resources/post-sections.tsx @@ -0,0 +1,85 @@ +import { Badge, Eyebrow, Reveal, Section } from "@nativelink/ui"; +import type { Post } from "../../lib/posts"; +import { formatPostDate } from "../../lib/posts"; + +export interface PostCardData { + key: string; + href: string; + external?: boolean; + tags: string[]; + meta: string; + title: string; + excerpt: string; + cta: string; +} + +export function postToCard(post: Post): PostCardData { + return { + key: post.slug, + href: `/resources/blog/${encodeURIComponent(post.slug)}`, + tags: post.tags, + meta: `${formatPostDate(post.pubDate)}${post.readTime ? ` · ${post.readTime}` : ""}`, + title: post.title, + excerpt: post.excerpt, + cta: "Read post", + }; +} + +export function PostCard({ card, index }: { card: PostCardData; index: number }) { + return ( + + +
    +
    + {card.tags.map((tag) => ( + + {tag} + + ))} + {card.meta} +
    +

    + {card.title} +

    +

    {card.excerpt}

    +
    +
    + {card.cta}{" "} + +
    +
    +
    + ); +} + +export function PostSection({ + title, + cards, + className, +}: { + title: string; + cards: PostCardData[]; + className?: string; +}) { + if (cards.length === 0) { + return null; + } + return ( +
    + + {title} + +
    + {cards.map((card, i) => ( + + ))} +
    +
    + ); +} diff --git a/web/apps/web/content/posts/Accelerating_CMake.mdx b/web/apps/web/content/posts/Accelerating_CMake.mdx new file mode 100644 index 000000000..153fc832b --- /dev/null +++ b/web/apps/web/content/posts/Accelerating_CMake.mdx @@ -0,0 +1,256 @@ +--- +title: "Supercharge Your C/C++ Builds without Migrating to Bazel: A Tutorial for CMake, Nativelink and BuildStream" +tags: ["tutorial", "cmake", "build-acceleration", "blog-posts"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/nativelink_logo.webp +slug: accelerating-cmake-with-nativelink +pubDate: 2025-08-12 +readTime: 8 minutes +--- + +## Supercharge Your C/C++ Builds without Migrating to Bazel: A Tutorial for CMake Acceleration with Nativelink and BuildStream + +Slow build times can be a major drag on developer productivity. Waiting for your code to compile and build can interrupt your flow and waste valuable time. Lots of companies have moved to Bazel and Buck2 to deal with this issue. But for many companies that investment in migrating to Bazel is too risky or too costly. What if you could accelerate your C/C++ projects using CMake? This tutorial will guide you through setting up a blazing-fast build environment using [Nativelink](https://github.com/TraceMachina/nativelink) and [Apache BuildStream](https://buildstream.build/) for remote caching and execution. We'll walk through a "hello, world" example to get you up and running in under 15 minutes. + +----- + +### What are Nativelink and Apache BuildStream? + + * **BuildStream** is a flexible and extensible tool for building and integrating software stacks. It creates a sandbox environment for your builds, ensuring they're reproducible and isolated. + * **Nativelink** is a high-performance remote execution and caching service that's compatible with the Remote Execution API (REAPI). It acts as a server to store and retrieve build artifacts, and to execute build tasks on remote workers. + +By using them together, you can distribute your build jobs and cache their results, leading to significant speedups, especially in large projects. + +----- + +### Project Setup + +Let's start by setting up our project directory. We'll create a structure to hold our source code, Makefile, and BuildStream configuration. + +```bash +mkdir -p first-project/elements/base first-project/files/src +cd first-project +``` + +Your directory structure should look like this: + +```shell +first-project/ +├── elements/ +│ └── base/ +│ +└── files/ + └── src/ +``` + +----- + +### The "Hello, World" Application + +Next, let's create our C application and the corresponding Makefile. + +#### `hello.c` + +Create a file named `hello.c` inside the `files/src` directory: + +```c +/* + * hello.c - a hello world program + */ +#include + +int main(int argc, char *argv[]) +{ + printf("Hello World\n"); + return 0; +} +``` + +#### `Makefile` + +Now, create a `Makefile` in the same `files/src` directory: + +```makefile +.PHONY: all + +all: hello +hello: hello.c + $(CC) -Wall -o $@ $< +``` + +----- + +### Configuring BuildStream + +Now, let's configure BuildStream to build our project. + +#### `project.conf` + +First, create a `project.conf` file in the root of your `first-project` directory. This file defines the basic properties of your project. + +```shell +# Unique project name +name: first-project + +# Required BuildStream version +min-version: 2.4 + +# Subdirectory where elements are stored +element-path: elements + +# Define an alias for our alpine tarball +aliases: + alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ +``` + +#### `elements/base/alpine.bst` + +Next, we need to define our base system. We'll use a pre-built, trimmed-down Alpine Linux image. Create `elements/base/alpine.bst`: + +```yaml +kind: import +description: | + + Alpine Linux base runtime + +sources: +- kind: tar + # This is a post doctored, trimmed down system image + # of the Alpine linux distribution. + # + url: alpine:integration-tests-base.v1.x86_64.tar.xz + ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 +``` + +#### `elements/base.bst` + +Now, create a `elements/base.bst` file to create a stack with the Alpine base: + +```yaml +kind: stack +description: Base stack + +depends: +- base/alpine.bst +``` + +####`elements/hello.bst` + +Finally, create the element for our "hello, world" application in `elements/hello.bst`. This element specifies how to build our application. + +```yaml +kind: manual +description: | + + Building manually + +# Depend on the base system +depends: +- base.bst + +# Stage the files/src directory for building +sources: + - kind: local + path: files/src + +# Now configure the commands to run +config: + + build-commands: + - make hello +``` + +----- + +### Configuring Nativelink for Remote Caching & Execution + +With our project set up, it's time to bring in Nativelink to accelerate the build. + +#### `buildstream.conf` + +To tell BuildStream to use a remote execution service, create a `buildstream.conf` file in your project's root directory: + +```yaml +cachedir: /tmp/buildstream + +artifacts: + servers: + - url: http://localhost:50051 + push: true +remote-execution: + execution-service: + url: http://localhost:50051 + action-cache-service: + url: http://localhost:50051 + storage-service: + url: http://localhost:50051 +``` + +This configuration tells BuildStream to connect to a Nativelink instance running on `localhost:50051` for artifact caching and remote execution. + +----- + +### Running the Build + +We'll use a script to launch the Nativelink service and then run the BuildStream build. The example below uses Nix to manage the dependencies, but you can adapt it to your environment. + +#### Launch Script + +Here is an example script, similar to `buildstream-with-nativelink-test.nix`, that shows how to run the build. + +```nix +{ + nativelink, + buildstream, + buildbox, + writeShellScriptBin, +}: +writeShellScriptBin "buildstream-with-nativelink-test" '' + set -uo pipefail + + cleanup() { + local pids=$(jobs -pr) + [ -n "$pids" ] && kill $pids + } + trap "cleanup" INT QUIT TERM EXIT + + ${nativelink}/bin/nativelink -- integration_tests/buildstream/buildstream_cas.json5 | tee -i integration_tests/buildstream/nativelink.log & + + # TODO(palfrey): PATH is workaround for https://github.com/NixOS/nixpkgs/issues/248000#issuecomment-2934704963 + bst_output=$(cd integration_tests/buildstream && PATH=${buildbox}/bin:$PATH ${buildstream}/bin/bst -c buildstream.conf build hello.bst 2>&1 | tee -i buildstream.log) + + case $bst_output in + *"SUCCESS Build"* ) + echo "Saw a successful buildstream build" + ;; + *) + echo 'Failed buildstream build:' + echo $bst_output + exit 1 + ;; + esac + + nativelink_output=$(cat integration_tests/buildstream/nativelink.log) + + case $nativelink_output in + *"ERROR"* ) + echo "Error in nativelink build" + exit 1 + ;; + *) + echo 'Successful nativelink build' + ;; + esac +'' +``` + +To run the build, you would execute this script. It first starts the `nativelink` service in the background, then runs `bst build`. + +The first time you run the build, it will compile the code and store the result in the Nativelink cache. Subsequent builds will be significantly faster as the results will be fetched directly from the cache, skipping the compilation step entirely. + +----- + +### Conclusion + +You have now successfully set up a project with BuildStream and Nativelink for accelerated builds with code pulled directly from Nativelink's production [integration tests](https://github.com/TraceMachina/nativelink/tree/main/integration_tests/buildstream). By leveraging remote caching and execution, you can dramatically reduce build times, especially for larger and more complex projects. This allows you to iterate faster and stay in the creative flow. Finally, developers and managers alike can appreciate a developer productivity tool! + +If you have any questions or want to learn more, feel free to reach out to **contact@nativelink.com**. Happy building\! 🚀 diff --git a/web/apps/web/content/posts/Adding_Support_for_Trust_Roots.mdx b/web/apps/web/content/posts/Adding_Support_for_Trust_Roots.mdx new file mode 100644 index 000000000..298227d9a --- /dev/null +++ b/web/apps/web/content/posts/Adding_Support_for_Trust_Roots.mdx @@ -0,0 +1,143 @@ +--- +title: "Trust Root Support in Nativelink" +tags: ["news", "blog-posts"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/nativelink_logo.webp +slug: adding-trust-roots-to-nativelink +pubDate: 2025-05-15 +readTime: 5 minutes +--- + +# Native Root Certificate Support in Nativelink + +**Open source thrives on community contributions, and today’s story is a perfect example of why.** +External engineer [Sam Eskandar](https://github.com/s6eskand) identified a pain point in Nativelink's TLS configuration and delivered [a clean solution](https://github.com/TraceMachina/nativelink/pull/1782) that eliminates deployment friction for teams using managed certificates. + +## Problem: Manual Certificate Management + +Before this change, connecting to gRPC endpoints using TLS required specifying [`ClientTlsConfig`](https://nativelink.com/docs/reference/nativelink-config/#clienttlsconfig) like this: + +```json +{ + ca_file: "path/to/ca.pem", + cert_file: "path/to/client.pem", + key_file: "path/to/client-key.pem" +} +``` + +Teams had to distribute CA certificates, client certificates, and key files across workers - creating friction for cloud deployments and potential security concerns when secrets need to be managed manually. + +## Solution: Native Root Certificate Support + +The new implementation adds a `use_native_roots` option that leverages the system's native root certificate store, eliminating manual certificate file management for most deployment scenarios. + +The core change is: + +```rust +if config.use_native_roots == Some(true) { + if config.ca_file.is_some() { + warn!("Native root certificates are being used, all certificate files will be ignored"); + } + return Ok(Some( + tonic::transport::ClientTlsConfig::new().with_native_roots(), + )); +} +``` + +This implementation provides three distinct behaviors: +- **Native roots enabled:** Use system native root certificates, ignore any provided certificate files +- **Manual certificate path:** Use existing manual certificate configuration +- **Clear validation:** Warn users when configurations conflict + +## Configuration Examples + +Here are the main ways to configure TLS in Nativelink. +For complete configuration options, see the [configuration reference](https://www.nativelink.com/docs/reference/nativelink-config). + +### Native Root Certificates + +For the majority of modern deployments, you can now enable native roots with one parameter: + +```json +{ + "tls_config": { + "use_native_roots": true + } +} +``` + +This configuration automatically trusts certificates signed by any certificate authority in your system’s trust store - perfect for cloud environments with managed certificates. + +### Manual Certificate Path + +Since `use_native_roots` defaults to `false`, you can still use the previous configuration: + +```json +{ + "tls_config": { + "ca_file": "/path/to/ca.pem", + "cert_file": "/path/to/client.pem", + "key_file": "/path/to/client-key.pem" + } +} +``` + +### gRPC Store and Local Worker Configuration + +The `tls_config` field can be used in gRPC store endpoints: + +```json +"stores": [ + { + "grpc": { + "endpoints": [ + { + "address": "grpcs://example.com:443", + "tls_config": { + "use_native_roots": true + } + } + ], + "instance_name": "main", + "store_type": "cas" + }, + "name": "CAS_STORE" + } +] +``` + +And in worker API endpoints: + +```json +"workers": [{ + "local": { + "worker_api_endpoint": { + "uri": "grpcs://127.0.0.1:50061", + "tls_config": { + "use_native_roots": true + } + }, + // ... + } +}] +``` + +## Community Contributions + +The [PR discussion](https://github.com/TraceMachina/nativelink/pull/1782) shows how the review process strengthened the implementation. +The contributor included comprehensive unit tests covering all configuration scenarios. +Reviewers suggested UX improvements like warning users when certificate files are ignored, identified documentation needs, and planned future enhancements. +One reviewer opened a follow-up PR to extend native roots to S3 stores. + +This collaborative refinement process caught edge cases, improved error handling, and identified future improvements - resulting in a more robust implementation than any single contributor could have produced alone. + +## Looking forward + +With native root certificate support, Nativelink becomes more accessible to teams across diverse infrastructure environments. +Whether you’re running containerized workloads in Kubernetes, deploying on traditional virtual machines with corporate PKI, or prototyping locally, TLS configuration no longer presents a barrier to adoption. + +**This is the power of open source in action** - community-driven improvements that make technology more accessible, secure, and flexible for everyone building the future. +We’re grateful for contributions like this that strengthen Nativelink’s position as the premier open source remote execution platform. + +--- + +*Interested in contributing? Check out our [GitHub repository](https://github.com/TraceMachina/nativelink).* diff --git a/web/apps/web/content/posts/Announcement_NativeLink.mdx b/web/apps/web/content/posts/Announcement_NativeLink.mdx new file mode 100644 index 000000000..1de14f928 --- /dev/null +++ b/web/apps/web/content/posts/Announcement_NativeLink.mdx @@ -0,0 +1,36 @@ +--- +title: "NativeLink - The free & open source simulation infrastructure platform written in Rust" +tags: ["news", "announcements"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/nativelink_open_source.webp +slug: NativeLink +readTime: 30 seconds +pubDate: 2024-09-17 +--- +Today, we're excited to release NativeLink–a Rust implementation of Bazel's +Remote Build Execution protocol (RBE) and Content Addressable Storage (CAS) +designed to run your code and get out of the way. NativeLink seamlessly +integrates with Bazel, Reclient, Goma, and Buck2. It comes at no cost and +works on every major operating system. + + +Offering a vendor neutral solution out the gate was important to us and we +will soon offer more options. Part of vendor neutrality began with +supporting four different build systems that implement the RBE protocol. +Given our focus on mission-critical systems with native code, +reproducibility and hermiticity have been our guiding lights. + + +Based on feedback from our earliest users, we made a few design choices +around the runtime, I/O, and software supply chain. We don't have any +garbage collection, supporting more deterministic and predictable +execution. In addition, the tunable and highly performant asynchronicity +afforded by Rust has allowed us to achieve concurrency and memory safety that were previously impracticable in most industries prior to its +creation. Ultimately, we strive for scale, modern SBOM transparency, and +verifiability. The software supply chain is the biggest threat to computer +security in 2023. For mission critical products, the risks are much +greater. Critical systems can't rely on closed source. + +Your contributions, questions, and community support are encouraged. Join +our Slack to learn more. + +The free & open source simulation infrastructure platform, in Rust diff --git a/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx b/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx new file mode 100644 index 000000000..c794bff5d --- /dev/null +++ b/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx @@ -0,0 +1,113 @@ +--- +title: "TraceMachina: Seed Funding" +tags: ["news", "announcements"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/tracemachina_seedfunding.webp +slug: tracemachina-seedfunding +pubDate: 2024-09-10 +readTime: 30 seconds +--- +Trace Machina secures funding and launches NativeLink out of stealth to +deliver simulation infrastructure for safety-critical technologies + +Today marks an exciting milestone for Trace Machina as we officially come +out of stealth mode and secured $4.7 million in seed funding to drive our +mission forward. + + +First, I am immensely grateful for the work that has been put in by the +entire Trace Machina team to get us here today. I’d like to thank our lead +investor, Van Jones from Wellington Management for believing in our vision +from the start. In addition to our partners at Samsung Next, Green Bay +Ventures, and Verissimo Ventures for everything they have done. +Instrumental angel investors and mentors including Clem Delangue, CEO of +Hugging Face, Mitch Wainer, Co-founder of DigitalOcean, Gert Lackriet, +Director of Applied Machine Learning at Amazon; and other industry leaders +from OpenAI and MongoDB. + + +Why we started Trace Machina +The inception of Trace Machina originated from a shared vision among a +group of passionate engineers and product leaders who have experience +developing cutting-edge technologies and solving complex problems in AI, +robotics, and autonomous systems at companies including Apple and Google. + + +Nathan Bruer, my Co-Founder, and I have always been driven by the challenge +of pushing the boundaries of technology. Nathan’s work at Google X on +autonomous driving software and my contributions to MongoDB Atlas Vector Search and other major open source projects laid the foundation for what +would become Trace Machina. We realized there was a significant gap in the +market for a robust simulation infrastructure that could support the +development of advanced systems. This realization led to the birth of Trace +Machina. + + +We're committed to developing tools that enable teams working on advanced +technologies like physical AI, specialized chip design, robotics, and +autonomous mobility. Our mission is to enable the next generation of +builders to create technology that has previously been unattainable or +uneconomical. + + +Launching NativeLink +NativeLink, our first product, embodies this mission. It's an open-source, +Rust-based simulation infrastructure platform designed to provide an +advanced simulation environment for technologies where safety is paramount. + + +NativeLink is the only platform for Bazel, Buck2, and Reclient written in +native code, tailored to handle large objects and intricate systems, across +native and interpreted programming languages. + + +From self-driving cars to aviation and robotics, NativeLink brings AI to +the edge, turning local devices into supercomputers and drastically +reducing cloud costs and accelerating builds. + + +NativeLink powers over one billion requests per month for some of the +largest companies in the world, providing speed and reliability in your +production workloads that unlocks possibilities previously unattainable +with any existing solution. + + +What's next +As we launch out of stealth, we're excited to share our vision and invite +the community to join us in building the future. + + +NativeLink is free and open source forever. With over a thousand stars on +GitHub and contributions from engineers at Tesla, General Motors, Samsung, +and others, we will continue to make NativeLink the best in class for +safety-critical technologies. Our focus will be on improving its +capabilities and expanding its adoption across various industries. + + +Building a strong, collaborative community is at the heart of our mission. +We encourage developers, engineers, and researchers to join our Slack, +contribute to our projects, and share their feedback. Together, we can +drive innovation and create a safer, more advanced future. + + +We’re already working on additional tools and infrastructure solutions to +address the evolving needs of developers in AI and autonomous systems. Stay +tuned for more announcements as we continue to innovate and push the +boundaries of what’s possible. + + +Trace Machina is more than just a company; it’s a movement. We invite you +to be a part of this exciting journey as we strive to revolutionize the +development of safety-critical technologies. + + +Thank you for your support. + + +Marcus Eagan + +CEO and Co-Founder, Trace Machina + + +For more information about Trace Machina and NativeLink, visit our website +and GitHub repository. + +Trace Machina launches NativeLink, announces seed round diff --git a/web/apps/web/content/posts/CaseStudy_CIQ.mdx b/web/apps/web/content/posts/CaseStudy_CIQ.mdx new file mode 100644 index 000000000..82582a898 --- /dev/null +++ b/web/apps/web/content/posts/CaseStudy_CIQ.mdx @@ -0,0 +1,121 @@ +--- +title: "Case Study: CIQ" +tags: ["news", "case-studies"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/nativelink_ciq.webp +slug: case-study-ciq +pubDate: 2024-08-13 +readTime: 2 minutes +--- +CIQ is an enterprise company that specializes in Linux distribution, +computing infrastructure, and cluster management and provisioning systems. +They have a deep running commitment to supporting open source projects and +creating enterprise-level support for them: They're the Founding Sponsor of +Rocky Linux, an open source operating system that rebuilds sources directly +from Red Hat Enterprise Linux (RHEL); they support an open source project +called Warewulf that's a cluster management and provisioning system to help +simplify deployment and management of compute clusters; and they supported +and donated Singularity (later named to Apptainer) to the Linux Foundation +that's designed to bring containers to high performance computing. + + +All of CIQ enterprise solutions, which are developed from their support and +contributions to the open source projects, are consolidated into one +repository, known as a monolithic repository (monorepo). A monorepo allows +CIQ to ensure all projects (solutions) are consistent and integrated +effectively, simplify dependency management as updates can be made +uniformly across the codebase, streamline CI/CD pipelines, and facilitate +cross-team collaboration and more. Since thousands of customers rely on +their solutions for mission-critical use cases, it’s imperative for them to +maintain the monorepo to: + +ensure consistency across any environment, architectures, libraries, +services, and tools +efficiently update and maintain features with no downtime +rapidly debug issues and patches across the entire codebase +perform comprehensive testing and validation across all projects +improve the scalability as the codebase grows + +CIQ needed a remote execution service to help dynamically scale their +compute resources, optimize build times, and handle increasing demands. + + +Storage Scalability Challenges +Physical disk resizing +CIQ previously relied on a solution where storage management was based on a +block-level architecture. Data was stored and accessed in fixed-size blocks +that’s directly tied to physical storage devices. The only way to increase +storage capacity is to physically resize the hard drives. As the data volume +increased, they had to consistently resize the disk. This impracticality was +time consuming and not scalable. When their previous solution couldn't handle +the demand and scale for their needs, CIQ turned to NativeLink for a more +robust and maintainable alternative. + + +Cloud-based storage +The other challenge CIQ faced was integrating with cloud-based distributed +storage systems like AWS S3 and Google Cloud Storage (GCS). This obstacle +primarily stemmed from legacy storage architecture where they heavily relied +on physical disk-based block storage. The block storage architecture couldn't +seamlessly interface with cloud-based distributed storage systems that are +designed to be elastic and offer on-demand scaling. This incompatibility made +it difficult for CIQ to take advantage of the cloud’s ability to adjust +storage capacity based on demand. They had high resource utilization waste +and couldn’t fully capitalize on cost performance benefits that cloud storage +provides. + + +Implementing and maintaining NativeLink +One of the standout features of NativeLink for the CIQ team was its intuitive +codebase housed in a single repository. NativeLink’s logical separation of +components enabled CIQ’s engineers to efficiently pinpoint the source of any +errors, and each component's responsibilities were well-defined, allowing for +swift problem resolution. The simplicity of NativeLink’s architecture meant +that most issues were addressed during the setup phase, minimizing the risk +of future errors. + +For CIQ, NativeLink is also incredibly low-touch when it comes to +maintenance. It provided a significant improvement in managing physical disk +storage compared to BuildBarn. Changing the physical disk sizes required +adjusting the Persistent Volume Claim (PVC) on Kubernetes (k8s) to reflect +the desired storage capacity and then update the storage configuration. This +streamlined CIQ’s process to quickly scale the physical disks with minimal +effort and adjust eviction policies. + + +Furthermore, NativeLink integrates seamlessly with cloud-based storage +systems, like S3. This enables CIQ to scale their infrastructure up or down +with flexibility and elasticity as their needs evolve. They now can +efficiently run multiple Content Addressable Storage (CAS) nodes. This +flexibility and reliability allowed CIQ to focus on more critical aspects of +their operations, knowing that their RBE system was stable and dependable. +For them, all of NativeLink’s setup was almost a “fire and forget” operation +for the CIQ team. + +The results +Operational savings - CIQ is able to avoid unnecessary provisioning of +resources and can scale their node pool with confidence. In addition, because +they no longer worry about the complexity of client-oriented scheduling, +higher Bazel jobs that would once overwhelm the system and cause timeouts and +other issues are handled with ease. +Efficient resource management - NativeLink has allowed CIQ to maintain a +consistent spot node pool of workers since deployment. Even with increased +activity, the system has remained stable, eliminating the need for constant +scaling. Additionally, NativeLink’s ability to handle all actions remotely +without the need to download CAS objects to GitHub Actions has provided +substantial savings in both time and resources. +Reduced CI and deployment times - Since implementing NativeLink, CIQ has been +able to reduce their CI times drastically, with average PR CI time from 15 +minutes to 3 minutes and full service deployment times dropping from an +average of over 1 hour to 15 minutes from code merge. + +Conclusion +Because of NativeLink, CIQ has been able to eliminate significant technical +challenges, see reduced operational costs, and increased efficiency. With +NativeLink’s intuitive architecture, ease of use, and minimal maintenance +requirements, CIQ has been able to scale with confidence and its engineers +empowered to focus on what they do best—building innovative solutions without +the distractions of an unreliable RBE system. + + +To learn more about NativeLink, read our documentation, check out our GitHub +Repository, or contact our team directly at hello@nativelink.com. diff --git a/web/apps/web/content/posts/CaseStudy_Fortune50.mdx b/web/apps/web/content/posts/CaseStudy_Fortune50.mdx new file mode 100644 index 000000000..1681fdc3e --- /dev/null +++ b/web/apps/web/content/posts/CaseStudy_Fortune50.mdx @@ -0,0 +1,28 @@ +--- +title: "Fortune 50 Manufacturer Uses NativeLink For 20x Accelerated Build Speed and 30x Improved Storage Density" +tags: ["news", "case-studies"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/nativelink_logo.webp +slug: case-study-fortune-50-manufacturer +pubDate: 2025-01-21 +readTime: 3 minutes +--- + +# Background + +A Fortune 50 Manufacturer builds a Chromium-based browser, and it has been an integral part of their product lineup, requiring robust and efficient build infrastructure to keep up with its evolving complexity. With a need for more advanced solutions due to increasing build complexity and domain variety, the Fortune 50 Manufacturer sought a tool that could provide remote execution and build caching at scale. + +## Challenges + +Increased build complexity. The heterogeneous nature of our customer’s builds required a solution capable of handling complex compositions across different executors. Existing solutions became inefficient and unscalable with increasing complexity of build domains, and the team needed a remote execution solution to push tasks to the right environments at the right time. + +## Solution + +Integrating with NativeLink’s remote execution platform and build caching capabilities, our client swiftly streamlined their build processes and reduced build times from 2 days to 2 **hours. Furthermore,** the customer team positioned itself to better handle future development needs, especially with the potential to improve storage density and overall system efficiency. + +Future-proofing development. By adopting NativeLink, our customer positioned itself to better handle future development needs, with the potential to improve storage density and overall system efficiency. In early tests, the customer team was able to achieve a 1:4 compression ratio on compressed content and a 30x increase on the storage density of edited CAS content by extending NativeLink's [Dedup store](https://github.com/TraceMachina/nativelink/blob/main/nativelink-store/src/dedup_store.rs). + +## Conclusion + +The adoption of NativeLink by the Fortune 50 Manufacturer has proven to be a game-changer, enabling the team to efficiently manage complex builds, reduce build times, and scale their infrastructure effectively. This integration highlights the potential of NativeLink to significantly enhance development processes for large-scale projects. + +To learn more about NativeLink, read our documentation, check out our GitHub Repository, or contact our team directly at [hello@nativelink.com](mailto:hello@nativelink.com). diff --git a/web/apps/web/content/posts/CaseStudy_LastMileAI.mdx b/web/apps/web/content/posts/CaseStudy_LastMileAI.mdx new file mode 100644 index 000000000..5542d8e2c --- /dev/null +++ b/web/apps/web/content/posts/CaseStudy_LastMileAI.mdx @@ -0,0 +1,38 @@ +--- +title: "Case Study: LastMile AI" +tags: ["news", "case-studies"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/lastmileai-logo.webp +slug: case-study-last-mile-ai +pubDate: 2025-09-25 +readTime: 2 minutes +--- +## **About LastMile AI** + +[LastMile AI](https://lastmileai.dev/) is an AI startup creating tools to enable developers to build AI agents. The team comes from backgrounds in top research and engineering organizations, building infrastructure and developer tools, and is backed by leading investors like Gradient Ventures, AME Cloud Ventures and Exceptional Capital. + +LastMile AI empowers developers to build and deploy their own AI agents using [**mcp-agent**](https://github.com/lastmile-ai/mcp-agent), a framework built on Model Context Protocol. mcp-agent has become a go-to toolkit for developers looking to connect large language models to real-world tools and workflows, and reflects the philosophy of empowering developers with transparent, composable systems. + +As mcp-agent has grown, scaling the infrastructure needed to support different libraries and tech stacks across thousands of developers has become increasingly demanding. + +## **Key Outcomes with NativeLink** + +* Eliminated repetitive local rebuilds across our engineering team. +* Enabled custom integrations with complex dependencies (like Temporal and Envoy) without build-time bottlenecks. +* Accelerated development of AI agents by streamlining interoperability across languages, allowing the best technology to be implemented for each task. +* Improved reliability of our platform while maintaining developer speed. + +## **The Challenge** + +LastMile AI relies heavily on a diverse range of technologies to power our platform, such as Temporal and Envoy, with a common denominator of protobuf and gRPC, to form the foundation of mcp-agent’s infrastructure, requiring interoperability across languages. In the world of AI, this challenge compounds: + +* **AI is uniquely polyglot.** While application code often lives in Python, the ecosystem depends critically on native libraries written in languages like Rust, C++, and CUDA. +* **Complex build targets.** To support diverse deployment environments, it’s often necessary to break apart dependencies and reconstruct them with Bazel. +* **Developer overhead.** Without a shared caching system, every engineer building their own AI agent would be forced to repeatedly recompile these large dependencies, slowing iteration, wasting time and frustrating developers. Shared remote caching lets the team treat foundational components as fixtures that benefit everyone instead of slowing down iteration. + +## **The Solution** + +NativeLink has eliminated much of the overhead needed in managing all the library dependencies. LastMile uses NativeLink as a remote build cache for Bazel: + +* **Eliminating repetitive local rebuilds.** Using NativeLink as a remote build cache for Bazel in our monorepo allows developers to reuse shared build outputs. +* **Incorporating heavier dependencies.** By caching builds, the team can incorporate heavier dependencies (such as Envoy and Temporal) directly into the build process. +* **Reproducible builds.** Because developers don’t have to waste time or resources with local rebuilds, they can get back to building. diff --git a/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx b/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx new file mode 100644 index 000000000..e0e197a12 --- /dev/null +++ b/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx @@ -0,0 +1,84 @@ +--- +title: "Thirdwave Automation and NativeLink" +tags: ["news", "case-studies"] +image: https://nativelink-cdn.s3.us-east-1.amazonaws.com/nativelink_logo.webp +slug: case-study-thirdwave-automation +pubDate: 2025-03-06 +readTime: 7 minutes +--- + + +#### + +#### *How NativeLink’s open software development and validation platform helped a robotics innovator deliver safer products faster (at less cost)* + +“Our business relies on delivering autonomous systems with an exceptional level of safety. With NativeLink’s massively-parallel cloud service, we can now iterate faster and continuously learn from our testing, without wasting time managing build cycles and cloud resources. It’s very fast, stable. It just works.” + +Nate Gallaher, DevOps Team Lead, Thirdwave Automation + +### Summary + +Founded in 2018 and funded by Toyota’s growth fund plus Innovation Endeavors, Norwest Venture Partners, Woven Capital, and Qualcomm Ventures, Third Wave Automation (TWA) leverages machine learning and artificial intelligence to manage fleets of automated forklifts and respond to edge cases in a timely and effective manner. + +Using automotive grade 3D LIDAR, TWA’s robotic forklifts incorporate Collision Shield, the industry-leading autonomous obstacle detection system running on forklifts. Other innovations include its Shared Autonomy Platform, enabling the TWA Reach line of forklifts to operate autonomously or seek help from remote operators who can take control from the safety of their office. + +Key elements of the company’s value proposition are safety, scalability, adaptability, repeatability, stability, and affordability. In order to preserve developer velocity and ensure timely delivery of its solutions, Thirdwave turned to NativeLink to help take its build and validation infrastructure to the next level. + +### Key Outcomes + +* 80% reduction in build times +* 50% reduction in cloud costs +* Transparent cloud scaling (Running 50K simultaneous jobs) +* Now testing daily vs. weekly or monthly +* Hundreds of developer hours saved per year +* Rapid deployment | Bazel compatible + +### The Challenge + +The company develops complex software for intelligent material-handing, including semi-autonomous robots as well as a fleet management system with remote operation and assistance capabilities. + +Written in C/C++ and Python, the TWA software is built upon a micro-services architecture that makes extensive use of containers. + +As the code base and number of developers have grown, the team's ability to iterate quickly was significantly hindered by prolonged build and test times, reducing developer productivity. + +But given the critical importance of safety, it became even more important to run testing on a more frequent basis, including unit tests, subsystem tests, and system-wide tests. Keeping developer velocity high by reducing test turnaround time was also a key requirement. + +At the same time, the company was concerned it was running compilation tasks on the same expensive GPUs as their ML tests. They were looking for a more flexible and configurable approach to save on cloud computing costs. + +### The Solution + +Massively parallel architecture with remote execution and caching + +Leveraging NativeLink’s remote execution and build caching platform, built on a massively-parallel, cloud-optimized service, the firm reduced build times by up to 80% while also removing developer overhead and friction. + +Incremental builds are also faster because cache artifacts are reused whenever possible. Developers never need to build or test the same thing twice and can share code across different environments. + +Nate reports that having faster cycles actually changed developer behavior by encouraging developers to run tests more frequently, without concerns about potential bottlenecks. + +Deterministic builds + +As a deterministic build system, NativeLink prevents cross-platform divergence by enforcing a hermetic toolchain and pinning all external dependencies to a given version, helping to prevent the common occurrence of “but it worked fine on my machine” syndrome. + +This also helps enforce critical governance controls around the software supply chain, preventing tampering and producing an SBOM required by compliance mandates. + +Open and extensible platform built on Bazel + +Licensed as Apache 2.0 open source, NativeLink seamlessly integrates with Bazel, the modern build client developed by Google and now used by 1,000+ organizations including Tesla, Nvidia, Ford, Stripe, Dropbox, Datadog, and Databricks. + +NativeLink works with all client-side build tools that support the Remote Bazel Execution (RBE) protocol such as Bazel, Buck2, Pantsbuild, and Reclient. + +You can customize NativeLink via JSON or YAML, as well as via Starlark, a Python-based declarative language for configuring Bazel with custom build rules and macros for specific projects and platforms. + +### About NativeLink + +NativeLink is the world's first open software development and validation platform purpose-built for diverse target platforms such as robots, autonomous vehicles, and edge devices. + +The platform is also optimized for developers building other types of large artifacts such as Chromium-based browsers, system software, and next-generation semiconductors. + +Designed to meet the growing scalability demands and complexity of large-scale software projects, NativeLink accelerates time-to-market by 10x or more without sacrificing reliability, safety, security, or efficiency. + +NativeLink is currently deployed in some of the world's largest and most complex environments, including Brex, Citrix, and Menlo Security. + +TraceMachina, developer of NativeLink, is backed by leading investors including Sequoia, Wellington Management, Verissimo Ventures, and prominent angel investors. + +To learn more about NativeLink, read our documentation, check out our GitHub Repository, or contact our team directly at [hello@nativelink.com](mailto:hello@nativelink.com). diff --git a/web/apps/web/content/posts/Finetune_LLM_On_CPU.mdx b/web/apps/web/content/posts/Finetune_LLM_On_CPU.mdx new file mode 100644 index 000000000..21f8e0cc0 --- /dev/null +++ b/web/apps/web/content/posts/Finetune_LLM_On_CPU.mdx @@ -0,0 +1,97 @@ +--- +title: "Fine-tune a Language Model on x86 CPUs using Bazel and NativeLink" +tags: ["news", "blog-posts"] +image: https://github.com/user-attachments/assets/ddfb5684-327b-4af9-9618-be707eab894f +slug: finetune-with-bazel-nativelink +pubDate: 2025-05-15 +readTime: 20 minutes +--- + +## Introduction + +The future of AI development belongs not necessarily to those with the most powerful infrastructure but to those who can extract maximum value from available resources. This tutorial emphasizes CPU-based fine-tuning demonstrating that with intelligent resource management through NativeLink, impressive results can be achieved without expensive GPU or TPU infrastructure. As compute becomes increasingly costly, competitive advantage will shift toward teams that optimize resource efficiency rather than those deploying state-of-the-art hardware. + +This guide demonstrates how to establish an optimized AI development pipeline by integrating several key technologies:

    +1\. **Bazel Build System**: For efficient repository management. A repository managed by Bazel allows your team to work in a unified codebase while maintaining clean separation of concerns.

    +2\. **NativeLink**: A remote execution system hosted in your cloud. With NativeLink's remote execution capabilities, you can leverage your cloud resources optimally without wasteful duplication of work.

    +3\. **Hugging Face Transformers**: For integrating with the rich ecosystem of open-source models that you can run locally or deploy anywhere. The transformers library also provides a sophisticated caching mechanism for optimizing loading model weights. + + +## Setting Up Your Repository With Bazel + +Bazel is a build system designed for repositories that allows you to organize code into logical components while maintaining dependency relationships. For AI workloads, this is particularly valuable as it lets you separate model definitions, data processing pipelines, training code, and inference services. + +### Prerequisites + +1\. A recent version of Bazel ([installation instructions](https://bazel.build/install)).

    +2\. [NativeLink 0.6.0](https://github.com/TraceMachina/nativelink/releases/tag/v0.6.0) (Apache-licensed) + +### Initial Setup + +First, let's download all the files. From the folder where you want to download the files, run the following commands: + +
    + +```bash +# Clone the entire repository +git clone https://github.com/TraceMachina/nativelink-blogs.git + +# Navigate to the subdirectory +cd nativelink-blogs/finetuning_on_cpu +``` +
    + + +Here's a description of some of the files:

    +1\. `README.md` - Instructions on how to connect/use NativeLink Cloud and how to run the code locally as well as remotely

    +2\. `requirements.lock` - Ensures consistent Python dependencies across all environments

    +3\. `.bazelrc` - Main Bazel configuration file setting global options for hermetic builds and remote execution

    +4\. `MODULE.bazel` - Configures the project as a Bazel module, tells Bazel we'll need Python, `pip` and CPU-only PyTorch, and manages external dependencies

    +5\. `pyproject.toml` - Python package configuration specifying dependencies and development tools

    +6\. `BUILD.bazel` (root) - Root build file defining lock targets for Python dependency management

    +7\. `platforms/BUILD` - Defines Linux x86_64 execution platform running in Ubuntu 24.04 for remote builds

    +8\. `training/BUILD` - Defines the model training targets and their dependencies

    +9\. `training/main.py` - Main script that handles fine-tuning of language models on CPU using efficient training techniques

    + + +## Important Note About Bazel’s Remote Execution Support + +Bazel supports remote execution for building (compiling) and testing, but `bazel run` uses the host platform as its target platform, meaning the executable will be invoked locally rather than on remote machines. To execute binaries on remote servers, a workaround is to design tests that execute the binary, effectively leveraging `bazel test` which runs on the target platform. + +**DRAWBACK:** + +Logging and print statements that track progress (like "Starting to fine tune model" or "Exiting this function") behave differently in remote execution. Unlike local runs where these appear in real-time, remote testing collects all logs on the server and only displays them after test completion when control returns to the local machine. The expected output is still fully preserved - just delayed until the process finishes running. + + +## Aside - Configuring Remote Execution For ARM (Apple Silicon) + + +Most cloud infrastructure (including NativeLink) runs on x86_64 processors, while Apple Silicon Macs use ARM64. Dependencies such as PyTorch are built for specified architectures and setting up Bazel to handle platform-specific dependencies is complex. + +If you want to run this code and you only have a Mac, the simplest way would be to run this code via a cloud-based Linux VM (GCP/AWS). If you don't want to use a cloud server, you could create a minimal docker container for x86_64 (`FROM --platform=linux/amd64 ubuntu:24.04` with just `curl` and `Bazelisk` installed) with **Rosetta enabled** and run from your Mac's terminal using this container. However, we highly recommend against this approach; the correct approach here would be to use toolchain transitions from a local mac platform to the remote Linux runner, but that's outside of the scope of this article. + + +## The NativeLink Difference + +To demonstrate NativeLink’s efficacy, consistency, and reliability, we ran the same fine-tuning job on the CPU of an M1 Pro MacBook Pro, the free version of Google Colab on CPU, and [NativeLink](https://github.com/TraceMachina/nativelink), which is free and open-source. We executed the fine-tuning task 5 times and this is what we observed: + +1\. The Mac: the quickest run took 18 minutes while the slowest/longest took 20 minutes + +2\. Free version of Google Colab: the quickest run took 10 minutes while the slowest/longest took 20 minutes. The execution time was widely varied. We suspect varying traffic on Google’s servers and how Colab allocates its compute resources played a part in this variability. + +3\. Free NativeLink: the quickest run took 4 minutes of compute time while the slowest/longest took 6 minutes. NativeLink Cloud provided the quickest execution times by far. + +Model Fine-Tuning Times + + +## Conclusion: Optimizing AI Development Through Resource Efficiency + +As demonstrated through this tutorial, the integration of Bazel's repository management, NativeLink's CPU-optimized remote execution, and Hugging Face's transformers library creates a development ecosystem that prioritizes computational efficiency over raw processing power. This approach addresses several critical challenges facing modern AI teams: + +1\. **Resource Optimization**: By leveraging NativeLink's intelligent scheduling and optimization on CPU infrastructure, teams can achieve impressive fine-tuning results without the capital expenditure of specialized GPU/TPU hardware.

    +2\. **Strategic Advantage**: This CPU-focused approach provides a competitive edge through efficient resource utilization, enabling teams to allocate budget toward innovation rather than hardware acquisition.

    +3\. **Sustainable Scaling**: As models grow in size and complexity, the ability to efficiently distribute workloads across existing CPU infrastructure provides a more sustainable path to scale than continuously upgrading to the latest accelerators.

    + +For forward-thinking AI teams, this infrastructure stack represents a shift from the "bigger is better" hardware arms race toward thoughtful resource utilization. The competitive advantage increasingly belongs to those who can extract maximum value from available compute rather than those who deploy more powerful hardware. + +The journey from experimental AI projects to production-grade systems demands both technical sophistication and resource awareness. By adopting this CPU-optimized approach with Bazel and NativeLink, your team can focus less on infrastructure limitations and more on the creative potential of fine-tuned models—developing applications that deliver genuine value while maintaining computational efficiency. diff --git a/web/apps/web/content/posts/NativeLink_for_Semiconductors.md b/web/apps/web/content/posts/NativeLink_for_Semiconductors.md new file mode 100644 index 000000000..073796898 --- /dev/null +++ b/web/apps/web/content/posts/NativeLink_for_Semiconductors.md @@ -0,0 +1,39 @@ +--- +title: "NativeLink for Semiconductors" +tags: ["news", "blog-posts"] +image: https://www.gstatic.com/webp/gallery/4.sm.webp +slug: semiconductors +pubDate: 2024-12-04 +readTime: 4 minutes +--- +## Open Source: Enabling the Performance and Deterministic Builds for the Next Era of Semiconductor Innovation + +### **Transforming Semiconductor Design with NativeLink** + +NativeLink has become a critical architectural component for companies developing custom silicon. From the outset, our mission was to build an open-source simulation platform designed to scale with cutting-edge client technologies and cater to the needs of pioneering industries like autonomous robotics. These innovators have increasingly sought alternatives to proprietary solutions for design simulation, leveraging NativeLink for direct hardware access, no garbage collection, considerable infrastructure cost reduction, and high-fidelity test environments. And now, with the proliferation of large language models (LLMs) and Nvidia’s monopolistic dominance in advanced computing, NativeLink has garnered significant interest from an unexpected sector: semiconductors. + +This shift is fueled by the proliferation of large language models (LLMs) and Nvidia's dominance in advanced computing, which have driven the development of new semiconductor technologies aimed at addressing supply-side challenges. Among the most promising advancements driving the development of new semiconductor technologies are: + +1. **Simplified Silicon Architectures**, streamlining traditional designs for efficiency and scalability. +2. **Diamond Wafers**, leveraging superior thermal and electrical properties. +3. **Nanophotonic Metamaterials**, pioneering optical solutions for enhanced performance. + +While these innovations hold transformative potential across industries from computing to healthcare, the legacy tools dominating electronic design automation (EDA)—such as Cadence, Ansys, and Synopsys—have anchored the space as billion-dollar leaders in EDA, ripe for smaller but hopefully valuable new integrations with their established, proprietary workflows. + +### **Open Silicon: Bridging the Gap with Open Source** + +The rising demand for compute power has outpaced the silicon industry's ability to deliver. To address this, Google developed the "Open Silicon" strategy, incorporating open-source technologies such as LLVM, XLS, OpenRoad, and Bazel. The traditional silicon design workflow—spanning RTL design, synthesis, and place-and-route—has historically relied on disparate, proprietary tools, creating inefficiencies in iteration and maintenance. + +By integrating these workflows into a Bazel-managed ecosystem, NativeLink enables a streamlined, deterministic approach. Using Bazel build rules written in Starlark, users can manage each design stage as version-controlled code within a monorepo, leveraging open-source tools for greater transparency, customizability, and collaboration. + +### **Deterministic Execution with Rust and Nix** + +NativeLink’s Rust foundation ensures reliability by minimizing race conditions, which are often identified at compile time. Coupled with Nix as a dependency manager, the platform provides a hermetic environment with fine-grained control over pinned dependencies. This approach contrasts with many legacy tools and emerging competitors that rely on garbage-collected languages, introducing nondeterministic behavior. + +### **A New Era of Open-Source EDA** + +Open-source tools such as Verilog, Bazel, Verilator, `rules_hdl`, and OpenRoad have set the stage for a new generation of silicon providers. RISC-V is new and not for everyone, but it's a glimpse into the opening world of semiconductors. By adopting an open source, instruction set architecture, companies can eliminate licensing fees and leverage modular design benefits. NativeLink furthers this modularity, breaking each system component into self-contained modules that foster maintainability and extensibility. For more details, [explore our GitHub repository](https://github.com/TraceMachina/nativelink). + +### **The Future of Semiconductor Innovation** + +NativeLink’s remote execution and caching capabilities are expanding rapidly, empowering innovators to explore the potential of open-source tools in semiconductor design. As industries push the boundaries of technology, we're committed to enabling breakthroughs with flexible, scalable, and deterministic infrastructure tools that engineers can rely on. diff --git a/web/apps/web/lib/posts.ts b/web/apps/web/lib/posts.ts new file mode 100644 index 000000000..4d9241839 --- /dev/null +++ b/web/apps/web/lib/posts.ts @@ -0,0 +1,104 @@ +import fs from "node:fs"; +import path from "node:path"; + +export interface Post { + slug: string; + title: string; + tags: string[]; + image?: string; + pubDate: string; + readTime?: string; + excerpt: string; + body: string; +} + +const POSTS_DIR = path.join(process.cwd(), "content/posts"); + +// The restored posts (recovered from the pre-redesign Astro site) all share +// the same flat frontmatter shape, so a full YAML parser isn't needed: +// title: "..." tags: [".."] image: url slug: str pubDate: date readTime: str +function parseFrontmatter(raw: string): { + data: Record; + body: string; +} { + const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/); + if (!match || match[1] === undefined) return { data: {}, body: raw }; + const data: Record = {}; + for (const line of match[1].split(/\r?\n/)) { + const kv = line.match(/^([A-Za-z]+):\s*(.*)$/); + const key = kv?.[1]; + if (!kv || key === undefined || kv[2] === undefined) continue; + const value = kv[2].trim(); + if (value.startsWith("[")) { + data[key] = value + .replace(/^\[|\]$/g, "") + .split(",") + .map((v) => v.trim().replace(/^["']|["']$/g, "")) + .filter(Boolean); + } else { + data[key] = value.replace(/^["']|["']$/g, ""); + } + } + return { data, body: raw.slice(match[0].length) }; +} + +function deriveExcerpt(body: string): string { + for (const block of body.split(/\r?\n\r?\n/)) { + let text = block + .replace(/^#+\s.*$/gm, "") + .replace(/^-{3,}\s*$/gm, "") + .replace(/!\[[^\]]*\]\([^)]*\)/g, "") + .replace(/\[([^\]]*)\]\([^)]*\)/g, "$1") + .replace(/[*_`>]/g, ""); + // Strip HTML tags to a fixpoint so overlapping fragments can't + // reassemble into a tag after a single pass. + let previous: string; + do { + previous = text; + text = text.replace(/<[^>]*>/g, ""); + } while (text !== previous); + text = text.replace(/\s+/g, " ").trim(); + if (text.length > 60) { + return text.length > 200 ? `${text.slice(0, 197).trimEnd()}…` : text; + } + } + return ""; +} + +export function getAllPosts(): Post[] { + return fs + .readdirSync(POSTS_DIR) + .filter((f) => /\.(md|mdx)$/.test(f)) + .map((file) => { + const raw = fs.readFileSync(path.join(POSTS_DIR, file), "utf8"); + const { data, body } = parseFrontmatter(raw); + const slug = + typeof data.slug === "string" && data.slug ? data.slug : file.replace(/\.(md|mdx)$/, ""); + return { + slug, + title: typeof data.title === "string" ? data.title : slug, + tags: Array.isArray(data.tags) ? data.tags : [], + image: typeof data.image === "string" ? data.image : undefined, + pubDate: typeof data.pubDate === "string" ? data.pubDate : "", + readTime: typeof data.readTime === "string" ? data.readTime : undefined, + excerpt: deriveExcerpt(body), + body, + }; + }) + .sort((a, b) => b.pubDate.localeCompare(a.pubDate)); +} + +export function getPost(slug: string): Post | undefined { + return getAllPosts().find((p) => p.slug === slug); +} + +export function formatPostDate(pubDate: string): string { + const date = new Date(`${pubDate}T00:00:00Z`); + if (Number.isNaN(date.getTime())) return pubDate; + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + timeZone: "UTC", + }); +} diff --git a/web/apps/web/package.json b/web/apps/web/package.json index f9af5467e..9a77d64a3 100644 --- a/web/apps/web/package.json +++ b/web/apps/web/package.json @@ -5,6 +5,7 @@ "@nativelink/tokens": "workspace:*", "@nativelink/ui": "workspace:*", "geist": "^1.5.1", + "marked": "^16.4.2", "motion": "^12.0.0", "next": "^16", "react": "^19.0.0", diff --git a/web/bun.lock b/web/bun.lock index 31f69219f..de3b65a22 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -43,6 +43,7 @@ "@nativelink/tokens": "workspace:*", "@nativelink/ui": "workspace:*", "geist": "^1.5.1", + "marked": "^16.4.2", "motion": "^12.0.0", "next": "^16", "react": "^19.0.0", From 6a162e37cf6315ad76638114129a31521e29356a Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Sun, 5 Jul 2026 09:20:54 -0700 Subject: [PATCH 015/144] evict .exec variant when its digest is evicted (#2474) (#2503) --- nativelink-store/src/filesystem_store.rs | 67 +++++++++++++++++++ .../tests/filesystem_store_test.rs | 50 ++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/nativelink-store/src/filesystem_store.rs b/nativelink-store/src/filesystem_store.rs index 521159044..03cf08fdc 100644 --- a/nativelink-store/src/filesystem_store.rs +++ b/nativelink-store/src/filesystem_store.rs @@ -786,6 +786,50 @@ where Ok(false) } +/// Deletes a digest's `.exec` variant (see +/// [`FilesystemStore::get_executable_hardlink_source`]) when that digest is +/// evicted or replaced in the primary CAS `evicting_map`. Without this, the +/// `.exec` directory is invisible to `max_bytes` and is only ever cleared by +/// the startup `remove_dir_all`, so it grows without bound at runtime (#2474). +/// Tying its lifetime to the primary entry instead bounds total disk use to +/// roughly `2 * max_bytes` in the worst case (every blob also executable). +#[cfg(unix)] +#[derive(Debug)] +struct ExecutableVariantRemover { + content_path: String, +} + +#[cfg(unix)] +impl RemoveItemCallback for ExecutableVariantRemover { + fn callback<'a>( + &'a self, + store_key: StoreKey<'a>, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let StoreKey::Digest(digest) = store_key else { + return; + }; + let variant_path = format!( + "{}{EXECUTABLE_DIR_SUFFIX}/{DIGEST_FOLDER}/{digest}", + self.content_path + ); + match fs::remove_file(&variant_path).await { + Ok(()) => debug!( + ?variant_path, + "Deleted executable variant for evicted digest" + ), + // Common case: no variant was ever materialized for this digest. + Err(err) if err.code == Code::NotFound => {} + Err(err) => warn!( + ?variant_path, + ?err, + "Failed to delete executable variant for evicted digest" + ), + } + }) + } +} + #[derive(Debug, MetricsComponent)] pub struct FilesystemStore { #[metric] @@ -850,6 +894,11 @@ impl FilesystemStore { fs::create_dir_all(format!("{executable_dir}/{DIGEST_FOLDER}")) .await .err_tip(|| format!("Failed to create executable dir {executable_dir}"))?; + evicting_map.add_remove_callback(RemoveItemCallbackHolder::new(Arc::new( + ExecutableVariantRemover { + content_path: spec.content_path.clone(), + }, + ))); } let shared_context = Arc::new(SharedContext { @@ -962,6 +1011,24 @@ impl FilesystemStore { } let result = self.create_executable_variant(digest, &variant_path).await; + + // The digest may have been evicted mid-copy: its eviction callback ran + // before the rename published the variant, so nothing owns the file + // anymore. This orphans the variant from eviction accounting, but the + // race is rare enough (needs an eviction to land in the narrow window + // between rename and this check, on a digest's first-ever variant + // materialization) that it's a self-limiting leak, not a systemic one + // — cheaper to log and let this action succeed with the still-valid + // file than to fail an otherwise-successful action over it. + if result.is_ok() && self.evicting_map.get(&digest.into()).await.is_none() { + warn!( + %digest, + ?variant_path, + "Digest evicted while materializing its executable variant; \ + variant is now untracked by eviction accounting" + ); + } + // Drop the per-digest lock entry regardless of outcome so the map // cannot grow unbounded; a concurrent waiter already cloned the Arc. self.forget_executable_lock(digest); diff --git a/nativelink-store/tests/filesystem_store_test.rs b/nativelink-store/tests/filesystem_store_test.rs index 90b571ea8..4810b722c 100644 --- a/nativelink-store/tests/filesystem_store_test.rs +++ b/nativelink-store/tests/filesystem_store_test.rs @@ -1663,6 +1663,56 @@ async fn executable_hardlink_source_created_once_and_readonly() -> Result<(), Er Ok(()) } +/// Regression test for #2474: the `.exec` variant directory was never +/// registered in `evicting_map`, so it was invisible to `max_bytes` and only +/// ever cleared by the startup `remove_dir_all` — growing without bound at +/// runtime. Evicting a digest from the primary CAS must also delete its +/// `.exec` sibling, bounding total `.exec` disk use to the primary store's +/// own eviction policy instead of letting it grow forever. +#[cfg(target_family = "unix")] +#[nativelink_test] +async fn evicting_digest_deletes_its_executable_variant() -> Result<(), Error> { + let content_path = make_temp_path("content_path"); + let temp_path = make_temp_path("temp_path"); + let digest1 = DigestInfo::try_new(HASH1, VALUE1.len())?; + let digest2 = DigestInfo::try_new(HASH2, VALUE2.len())?; + + let store = Box::pin( + FilesystemStore::::new(&FilesystemSpec { + content_path: content_path.clone(), + temp_path: temp_path.clone(), + eviction_policy: Some(EvictionPolicy { + max_count: 1, + ..Default::default() + }), + ..Default::default() + }) + .await?, + ); + store.update_oneshot(digest1, VALUE1.into()).await?; + + let variant_path = OsString::from(format!("{content_path}.exec/{DIGEST_FOLDER}/{digest1}")); + store.get_executable_hardlink_source(&digest1).await?; + fs::metadata(&variant_path) + .await + .err_tip(|| "Executable variant should exist right after creation")?; + + // max_count: 1 means inserting a second digest evicts the first from the + // primary evicting_map, which must also delete digest1's `.exec` sibling. + store.update_oneshot(digest2, VALUE2.into()).await?; + + let err = fs::metadata(&variant_path) + .await + .expect_err("Executable variant must be deleted when its digest is evicted"); + assert_eq!( + err.code, + Code::NotFound, + "Expected the executable variant to be gone, got: {err:?}" + ); + + Ok(()) +} + /// This test simulates a full disk without needing one. It writes past the `RLIMIT_FSIZE` /// cap, thus failing with `EFBIG`, which tokio defers exactly like `ENOSPC`. The /// [`SIGXFSZ`](`libc::SIGXFSZ`) signal must be ignored or the kernel will kill the process From 0d516b903054f6fe320767a8c88203137c374c31 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 10:34:57 +0100 Subject: [PATCH 016/144] Update dependency marked to v18 (#2502) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- web/apps/web/package.json | 2 +- web/bun.lock | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web/apps/web/package.json b/web/apps/web/package.json index 9a77d64a3..bfb6896de 100644 --- a/web/apps/web/package.json +++ b/web/apps/web/package.json @@ -5,7 +5,7 @@ "@nativelink/tokens": "workspace:*", "@nativelink/ui": "workspace:*", "geist": "^1.5.1", - "marked": "^16.4.2", + "marked": "^18.0.0", "motion": "^12.0.0", "next": "^16", "react": "^19.0.0", diff --git a/web/bun.lock b/web/bun.lock index de3b65a22..2192f4bbf 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -43,7 +43,7 @@ "@nativelink/tokens": "workspace:*", "@nativelink/ui": "workspace:*", "geist": "^1.5.1", - "marked": "^16.4.2", + "marked": "^18.0.0", "motion": "^12.0.0", "next": "^16", "react": "^19.0.0", @@ -772,7 +772,7 @@ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], - "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + "marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="], "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], @@ -1078,6 +1078,8 @@ "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], From d624f6a483a7b5530506c3ca76d8890bae269239 Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Mon, 6 Jul 2026 16:22:58 +0530 Subject: [PATCH 017/144] Update the OCI store with the tests and docs (#2506) --- nativelink-store/src/oci_store.rs | 28 ++- nativelink-store/tests/oci_store_test.rs | 219 +++++++++++++++++- .../docs/content/docs/deployment/meta.json | 1 + .../docs/deployment/oci-object-storage.mdx | 139 +++++++++++ 4 files changed, 382 insertions(+), 5 deletions(-) create mode 100644 web/apps/docs/content/docs/deployment/oci-object-storage.mdx diff --git a/nativelink-store/src/oci_store.rs b/nativelink-store/src/oci_store.rs index 942c419cd..3517690ba 100644 --- a/nativelink-store/src/oci_store.rs +++ b/nativelink-store/src/oci_store.rs @@ -20,7 +20,10 @@ use aws_config::default_provider::credentials::DefaultCredentialsChain; use aws_config::provider_config::ProviderConfig; use aws_config::{AppName, BehaviorVersion}; use aws_sdk_s3::Client; -use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::config::{ + Credentials, Region, RequestChecksumCalculation, ResponseChecksumValidation, +}; +use aws_smithy_runtime_api::client::http::HttpClient as SmithyHttpClient; use nativelink_config::stores::{ExperimentalAwsSpec, ExperimentalOciSpec}; use nativelink_error::Error; use nativelink_util::instant_wrapper::InstantWrapper; @@ -51,11 +54,28 @@ impl OciStore { where I: InstantWrapper, NowFn: Fn() -> I + Send + Sync + Unpin + 'static, + { + Self::new_with_http_client(spec, TlsClient::new(&spec.common.clone()), now_fn).await + } + + /// Builds the store with a caller-supplied HTTP client. Production uses + /// [`Self::new`] (which injects a [`TlsClient`]); tests inject a mock (e.g. + /// `StaticReplayClient`) to exercise the OCI-specific wire behavior — + /// path-style URLs and the absence of `aws-chunked` — without a live bucket. + #[allow(clippy::new_ret_no_self)] + pub async fn new_with_http_client( + spec: &ExperimentalOciSpec, + http_client: C, + now_fn: NowFn, + ) -> Result>, Error> + where + I: InstantWrapper, + NowFn: Fn() -> I + Send + Sync + Unpin + 'static, + C: SmithyHttpClient + Clone + 'static, { let aws_spec = Self::build_aws_spec(spec); let jitter_fn = spec.common.retry.make_jitter_fn(); - let http_client = TlsClient::new(&spec.common.clone()); let endpoint = Self::derive_endpoint(spec); let region = Region::new(Cow::Owned(spec.region.clone())); @@ -72,6 +92,10 @@ impl OciStore { // OCI's compatibility endpoint embeds the bucket in the path, not as // a subdomain, so virtual-hosted addressing must be disabled. .force_path_style(true) + // OCI does not support the AWS SDK's default flexible-checksum + // behavior. + .request_checksum_calculation(RequestChecksumCalculation::WhenRequired) + .response_checksum_validation(ResponseChecksumValidation::WhenRequired) .http_client(http_client.clone()); config_builder = if let Some(key_id) = &spec.access_key_id diff --git a/nativelink-store/tests/oci_store_test.rs b/nativelink-store/tests/oci_store_test.rs index 16f1c8f8e..d2eb5cadb 100644 --- a/nativelink-store/tests/oci_store_test.rs +++ b/nativelink-store/tests/oci_store_test.rs @@ -12,13 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! `OciStore` builds an SDK client and hands it to `S3Store`. The S3 wire -//! behaviour is covered by `s3_store_test.rs`. +//! `OciStore` builds an SDK client and hands it to `S3Store`. These tests cover +//! the config -> client mapping and the OCI-specific wire behavior (path-style +//! URLs and non-`aws-chunked` uploads) using a `StaticReplayClient`, all without +//! a live bucket. The generic S3 wire behavior is covered by `s3_store_test.rs`; +//! live round-trip / multipart / load coverage is kept out of the repo. +use std::sync::Arc; + +use aws_smithy_http_client::test_util::{ReplayEvent, StaticReplayClient}; +use aws_smithy_types::body::SdkBody; +use http::{StatusCode, header}; use nativelink_config::stores::{CommonObjectSpec, ExperimentalOciSpec}; -use nativelink_error::Error; +use nativelink_error::{Code, Error}; use nativelink_macro::nativelink_test; use nativelink_store::oci_store::OciStore; +use nativelink_store::s3_store::S3Store; +use nativelink_util::common::DigestInfo; +use nativelink_util::instant_wrapper::MockInstantWrapped; +use nativelink_util::store_trait::StoreLike; use pretty_assertions::assert_eq; #[nativelink_test] @@ -57,3 +69,204 @@ async fn aws_spec_passes_region_bucket_and_common_through() -> Result<(), Error> assert_eq!(aws_spec.common.consider_expired_after_s, 86400); Ok(()) } + +// --------------------------------------------------------------------------- +// Mock wire-behavior tests. Drive the store through `new_with_http_client` with +// a `StaticReplayClient` to verify the OCI-specific request shaping without a +// live bucket. +// --------------------------------------------------------------------------- + +const NAMESPACE: &str = "ns"; +const REGION: &str = "us-region-1"; +const BUCKET: &str = "test-bucket"; +const HOST: &str = "ns.compat.objectstorage.us-region-1.oci.customer-oci.com"; +const VALID_HASH1: &str = "0123456789abcdef000000000000000000010000000000000123456789abcdef"; + +// Coerces the function item to a plain fn pointer matching the store type. +const NOW_FN: fn() -> MockInstantWrapped = MockInstantWrapped::default; + +fn mock_spec() -> ExperimentalOciSpec { + ExperimentalOciSpec { + namespace: NAMESPACE.to_string(), + region: REGION.to_string(), + bucket: BUCKET.to_string(), + access_key_id: Some("AKIDTEST".to_string()), + secret_access_key: Some("SECRETTEST".to_string()), + ..Default::default() + } +} + +async fn store_with( + mock: StaticReplayClient, +) -> Result MockInstantWrapped>>, Error> { + OciStore::new_with_http_client(&mock_spec(), mock, NOW_FN).await +} + +#[nativelink_test] +async fn has_object_found_uses_path_style_endpoint() -> Result<(), Error> { + const SIZE: u64 = 512; + let mock = StaticReplayClient::new(vec![ReplayEvent::new( + http::Request::builder() + .method("HEAD") + .uri(format!("https://{HOST}/{BUCKET}/{VALID_HASH1}-{SIZE}")) + .body(SdkBody::empty()) + .unwrap(), + http::Response::builder() + .status(StatusCode::OK) + .header(header::CONTENT_LENGTH, SIZE.to_string()) + .body(SdkBody::empty()) + .unwrap(), + )]); + let store = store_with(mock.clone()).await?; + + let result = store.has(DigestInfo::try_new(VALID_HASH1, SIZE)?).await?; + assert_eq!(result, Some(SIZE), "expected object to be found"); + mock.assert_requests_match(&[]); + Ok(()) +} + +#[nativelink_test] +async fn has_object_not_found_returns_none() -> Result<(), Error> { + let mock = StaticReplayClient::new(vec![ReplayEvent::new( + http::Request::builder().body(SdkBody::empty()).unwrap(), + http::Response::builder() + .status(StatusCode::NOT_FOUND) + .body(SdkBody::empty()) + .unwrap(), + )]); + let store = store_with(mock).await?; + + let result = store.has(DigestInfo::try_new(VALID_HASH1, 100)?).await?; + assert_eq!(result, None, "expected absent object to map to None"); + Ok(()) +} + +#[nativelink_test] +async fn get_missing_object_maps_to_not_found() -> Result<(), Error> { + // OCI (like S3) returns a NoSuchKey error document on a missing GET; the SDK + // parses it and the store maps it to `Code::NotFound`. + const NO_SUCH_KEY: &str = concat!( + "", + "NoSuchKey", + "The specified key does not exist." + ); + let mock = StaticReplayClient::new(vec![ReplayEvent::new( + http::Request::builder().body(SdkBody::empty()).unwrap(), + http::Response::builder() + .status(StatusCode::NOT_FOUND) + .body(SdkBody::from(NO_SUCH_KEY)) + .unwrap(), + )]); + let store = store_with(mock).await?; + + let err = store + .get_part_unchunked(DigestInfo::try_new(VALID_HASH1, 100)?, 0, None) + .await + .expect_err("get on a missing object must error"); + assert_eq!( + err.code, + Code::NotFound, + "expected NotFound code for an absent object" + ); + Ok(()) +} + +#[nativelink_test] +async fn update_single_put_is_path_style_and_unchunked() -> Result<(), Error> { + const DATA: &[u8] = b"hello-oci-single-put-body"; + let mock = StaticReplayClient::new(vec![ReplayEvent::new( + http::Request::builder().body(SdkBody::empty()).unwrap(), + http::Response::builder() + .status(StatusCode::OK) + .body(SdkBody::empty()) + .unwrap(), + )]); + let store = store_with(mock.clone()).await?; + + store + .update_oneshot( + DigestInfo::try_new(VALID_HASH1, DATA.len() as u64)?, + DATA.into(), + ) + .await?; + + let reqs: Vec<_> = mock.actual_requests().collect(); + assert_eq!(reqs.len(), 1, "expected exactly one PutObject request"); + let req = &reqs[0]; + assert_eq!(req.method(), "PUT", "single-shot upload should be a PUT"); + let want_prefix = format!("https://{HOST}/{BUCKET}/{VALID_HASH1}-{}", DATA.len()); + assert!( + req.uri().starts_with(&want_prefix), + "expected path-style URL starting with {want_prefix}, got {}", + req.uri() + ); + let sha = req + .headers() + .get("x-amz-content-sha256") + .unwrap_or_default(); + assert!( + !sha.contains("STREAMING") && !sha.contains("CHUNKED"), + "PutObject must not use aws-chunked payload signing, got x-amz-content-sha256={sha}" + ); + assert_ne!( + req.headers().get("content-encoding"), + Some("aws-chunked"), + "PutObject must not set Content-Encoding: aws-chunked" + ); + Ok(()) +} + +#[nativelink_test] +async fn get_object_returns_bytes() -> Result<(), Error> { + const VALUE: &str = "oci-object-contents"; + let mock = StaticReplayClient::new(vec![ReplayEvent::new( + http::Request::builder() + .uri(format!( + "https://{HOST}/{BUCKET}/{VALID_HASH1}-1000?x-id=GetObject" + )) + .body(SdkBody::empty()) + .unwrap(), + http::Response::builder() + .status(StatusCode::OK) + .body(SdkBody::from(VALUE)) + .unwrap(), + )]); + let store = store_with(mock.clone()).await?; + + let got = store + .get_part_unchunked(DigestInfo::try_new(VALID_HASH1, 1000)?, 0, None) + .await?; + assert_eq!(got, VALUE.as_bytes()); + mock.assert_requests_match(&[]); + Ok(()) +} + +#[nativelink_test] +async fn ranged_get_sends_range_header_path_style() -> Result<(), Error> { + const OFFSET: usize = 105; + const LENGTH: usize = 50_000; + let mock = StaticReplayClient::new(vec![ReplayEvent::new( + http::Request::builder() + .uri(format!( + "https://{HOST}/{BUCKET}/{VALID_HASH1}-1000?x-id=GetObject" + )) + .header("range", format!("bytes={OFFSET}-{}", OFFSET + LENGTH)) + .body(SdkBody::empty()) + .unwrap(), + http::Response::builder() + .status(StatusCode::OK) + .body(SdkBody::empty()) + .unwrap(), + )]); + let store = store_with(mock.clone()).await?; + + store + .get_part_unchunked( + DigestInfo::try_new(VALID_HASH1, 1000)?, + OFFSET as u64, + Some(LENGTH as u64), + ) + .await?; + mock.assert_requests_match(&[]); + Ok(()) +} diff --git a/web/apps/docs/content/docs/deployment/meta.json b/web/apps/docs/content/docs/deployment/meta.json index 7c04eb918..820895be8 100644 --- a/web/apps/docs/content/docs/deployment/meta.json +++ b/web/apps/docs/content/docs/deployment/meta.json @@ -2,6 +2,7 @@ "pages": [ "on-prem-overview", "kubernetes", + "oci-object-storage", "chromium", "metrics", "persistent-workers" diff --git a/web/apps/docs/content/docs/deployment/oci-object-storage.mdx b/web/apps/docs/content/docs/deployment/oci-object-storage.mdx new file mode 100644 index 000000000..381878bbe --- /dev/null +++ b/web/apps/docs/content/docs/deployment/oci-object-storage.mdx @@ -0,0 +1,139 @@ +--- +title: Oracle Cloud (OCI) Object Storage +description: Back NativeLink's CAS and Action Cache with Oracle Cloud Infrastructure Object Storage via its S3 Compatibility API. +--- + +NativeLink can use [Oracle Cloud Infrastructure (OCI) Object +Storage](https://www.oracle.com/cloud/storage/object-storage/) as the backing +store for the CAS and Action Cache. It is configured through the +`experimental_cloud_object_store` store with `provider: "oci"`, alongside the +`aws`, `gcs`, `azure`, and `r2` providers. + +## How it works + +OCI Object Storage exposes an [S3 Compatibility +API](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm). +The OCI store is a thin adapter that points NativeLink's S3 store +at OCI's S3-compatible endpoint, so it reuses the same multipart upload, retry, +and streaming code paths as the AWS backend. Two OCI-specific adjustments are +applied automatically: + +- **Path-style addressing** - OCI requires `endpoint/bucket/key` rather than + virtual-hosted (`bucket.endpoint/key`) URLs. +- **Checksums downgraded to "when required"** - the AWS SDK otherwise adds a + default trailing checksum that forces `Content-Encoding: aws-chunked`, which + OCI rejects with `501 NotImplemented: AWS chunked encoding not supported`. + Without this, small single-`PUT` objects (notably Action Cache entries) fail. + +You do not need to configure either of these; they are handled by the store. + +## Prerequisites + +1. **A bucket.** Create one in the OCI Console under *Storage → Buckets*, or + with `oci os bucket create`. Note the bucket's **region** (e.g. + `ap-mumbai-1`). +2. **Your Object Storage namespace.** A tenancy-wide identifier shown under + *Tenancy details*, or via `oci os ns get`. The endpoint is derived as + `https://{namespace}.compat.objectstorage.{region}.oci.customer-oci.com`. +3. **A Customer Secret Key** (see [Authentication](#authentication)). + +## Authentication + +OCI's S3 Compatibility API authenticates with **Customer Secret Keys** - a +static access-key / secret-key pair, used as the S3 `access_key_id` and +`secret_access_key`. + +Generate one in the Console under *Profile → Customer Secret Keys → Generate +Secret Key*: + +- The **Secret Key** is shown **only once** at generation time - copy it + immediately. +- The **Access Key** is listed permanently in the Customer Secret Keys table and + can be copied at any time. + +Grant the associated user read/write on the bucket with a least-privilege +policy: + +``` +Allow group to manage object-family in compartment where target.bucket.name='' +``` + +We recommend supplying the keys via environment variables and referencing them +with shellexpand. + + + The S3 Compatibility API supports **only** Customer Secret Keys. OCI-native + authentication - API signing keys, and instance/resource principals - is **not** + available through this store. If you run NativeLink on OCI compute and want + keyless workload-identity auth, that is not currently supported; you must use + static Customer Secret Keys and rotate them yourself. + + +## Configuration + +A minimal CAS + Action Cache setup (see also +[`oci_backend.json5`](https://github.com/TraceMachina/nativelink/blob/main/nativelink-config/examples/oci_backend.json5)): + +```json5 +{ + stores: [ + { + name: "CAS_MAIN_STORE", + experimental_cloud_object_store: { + provider: "oci", + namespace: "storagenamespace", + region: "ap-mumbai-1", + bucket: "nativelink-cas", + access_key_id: "${OCI_ACCESS_KEY_ID}", + secret_access_key: "${OCI_SECRET_ACCESS_KEY}", + key_prefix: "cas/", + retry: { max_retries: 6, delay: 0.3, jitter: 0.5 }, + }, + }, + { + name: "AC_MAIN_STORE", + experimental_cloud_object_store: { + provider: "oci", + namespace: "storagenamespace", + region: "ap-mumbai-1", + bucket: "nativelink-cas", + access_key_id: "${OCI_ACCESS_KEY_ID}", + secret_access_key: "${OCI_SECRET_ACCESS_KEY}", + key_prefix: "ac/", + retry: { max_retries: 6, delay: 0.3, jitter: 0.5 }, + }, + }, + ], + // ...servers omitted; see oci_backend.json5 +} +``` + +In production, wrap the CAS store in `verify` (for integrity) and front it with +a `fast_slow` filesystem/memory layer to cut round-trips to OCI, exactly as you +would for the AWS backend. + +### Fields + +| Field | Required | Description | +|-------|----------|-------------| +| `namespace` | yes | Object Storage namespace; used to derive the endpoint. | +| `region` | yes | OCI region id (e.g. `ap-mumbai-1`); also the SigV4 signing region. | +| `bucket` | yes | Target bucket. | +| `access_key_id` | no | Customer Secret Key access key. Falls back to the AWS default credential chain if unset. | +| `secret_access_key` | no | Customer Secret Key secret. | +| `key_prefix`, `retry`, ... | no | Shared `experimental_cloud_object_store` options. | + +## Operational notes + +- **Reap incomplete multipart uploads.** When a multipart upload fails + mid-flight the store issues `AbortMultipartUpload` to clean up (verified that + OCI honors both `ListMultipartUploads` and `AbortMultipartUpload`). A process + killed *before* it can abort still leaves orphaned parts, so add a bucket + lifecycle rule to abort incomplete multipart uploads after a few days as a + backstop. +- **Status.** This backend lives under `experimental_cloud_object_store`. Its + core paths - read, write, existence checks, ranged reads, multipart, and + integrity - are verified against live OCI, including an end-to-end Bazel remote + cache round-trip, a 2 GiB multipart upload (~400 parts) with full byte + verification, multipart abort/cleanup, and 200-way concurrent load. It has not + been benchmarked at sustained production scale or duration. From 87d16c6a33c4a75add5db499d8a2aa4a84750de1 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Mon, 6 Jul 2026 13:45:02 +0100 Subject: [PATCH 018/144] Strip stray item from changelog (#2507) --- CHANGELOG.md | 4 ---- cliff.toml | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45e5b7b3c..f6bb9914c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,10 +40,6 @@ All notable changes to this project will be documented in this file. - Make bash shell scripts actually fall over ([#2428](https://github.com/TraceMachina/nativelink/issues/2428)) - ([d537003](https://github.com/TraceMachina/nativelink/commit/d537003b2eb7ccae69bd126fbec4ea4a9e73aa03)) - worker_utils test coverage to 100% ([#2427](https://github.com/TraceMachina/nativelink/issues/2427)) - ([729c88d](https://github.com/TraceMachina/nativelink/commit/729c88dcf65dd40da4736ddf328fb6a2dbbecd63)) -### ⚙️ Miscellaneous - -- wtf did this come from ([#2434](https://github.com/TraceMachina/nativelink/issues/2434)) - ([6abc5d8](https://github.com/TraceMachina/nativelink/commit/6abc5d87e3127ec8eaf7b8a802c47e2aa3a4f7e2)) - ## [1.5.0](https://github.com/TraceMachina/nativelink/compare/v1.4.0..v1.5.0) - 2026-06-12 ### ⛰️ Features diff --git a/cliff.toml b/cliff.toml index 46c513a3e..b0fa1ba5a 100644 --- a/cliff.toml +++ b/cliff.toml @@ -137,6 +137,7 @@ commit_parsers = [ { message = "Merge branch", skip = true }, { message = "Prepare.+release", skip = true }, { message = "Release", skip = true }, + { message = "wtf", skip = true }, # Catch-all in miscellaneous { message = ".*", group = "⚙️ Miscellaneous" }, From 5e72a32f2b07f5ce5c6b03eded08c7380999cafe Mon Sep 17 00:00:00 2001 From: Keshav Arora Date: Mon, 6 Jul 2026 19:49:26 +0530 Subject: [PATCH 019/144] Add troubleshooting tips for macOS Nix setup (#2505) --- README.md | 12 ++++++++++++ web/apps/docs/content/docs/contribute/nix.mdx | 12 ++++++++++++ web/apps/docs/content/docs/getting-started/setup.mdx | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9068c532..addcc6ff0 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,18 @@ it via the [next-gen nix installer](https://github.com/NixOS/experimental-nix-in > Executables built for MacOS are dynamically linked against libraries from Nix > and won't work on systems that don't have these libraries present. +> [!TIP] +> **Common setup gotchas for Nix on macOS / Linux:** +> * **Active shell environment**: If the installer finishes but your shell doesn't recognize `nix` commands, you need to either restart your terminal session or source the daemon profile manually: +> ```bash +> . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh +> ``` +> * **Enabling experimental features**: If you used the standard Nix installer and get an error saying `experimental Nix feature 'nix-command' is disabled`, enable them by creating or editing `~/.config/nix/nix.conf`: +> ```text +> experimental-features = nix-command flakes +> ``` +> * **Disk Space**: Unpacking and building compilers and dependencies requires significant storage. Make sure you have at least **15–20 GB of free space** on your system volume before running setup commands. + **Linux, MacOS, WSL2** ```bash diff --git a/web/apps/docs/content/docs/contribute/nix.mdx b/web/apps/docs/content/docs/contribute/nix.mdx index 533b6b4de..db332cb25 100644 --- a/web/apps/docs/content/docs/contribute/nix.mdx +++ b/web/apps/docs/content/docs/contribute/nix.mdx @@ -22,6 +22,18 @@ curl --proto '=https' --tlsv1.2 -sSf \ It enables flakes by default. + + - **Environment Paths**: If your shell doesn't recognize `nix` commands immediately after installation, you need to restart your terminal or run: + ```bash + . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh + ``` + - **Experimental Features**: If you used the standard Nix installer and get `experimental Nix feature 'nix-command' is disabled`, enable them by adding the following to your `~/.config/nix/nix.conf` file: + ```text + experimental-features = nix-command flakes + ``` + - **Disk Space**: Unpacking toolchains, compiler binaries, and caching build artifacts requires considerable storage. Ensure you have at least **15–20 GB of free space** on your drive before running `nix develop`. + + ## Enter the dev shell ```bash diff --git a/web/apps/docs/content/docs/getting-started/setup.mdx b/web/apps/docs/content/docs/getting-started/setup.mdx index 4585ebd67..43a103bea 100644 --- a/web/apps/docs/content/docs/getting-started/setup.mdx +++ b/web/apps/docs/content/docs/getting-started/setup.mdx @@ -50,7 +50,9 @@ macOS (Apple Silicon and Intel) and any Linux with Nix. Also the only path that supports Apple Silicon natively at the moment. - Executables built for macOS link dynamically against libraries from the + - **Prerequisites**: Make sure your Nix installation has experimental features enabled (add `experimental-features = nix-command flakes` to your `~/.config/nix/nix.conf`). + - **Disk Space**: The installation and build process requires downloading and compiling toolchains and compilers. Ensure you have at least **15–20 GB of free space** on your drive. + - **Portability**: Executables built for macOS link dynamically against libraries from the Nix store. They will not run on systems without those libraries available. Stick to the Docker path if you need to copy the binary to another machine. From d9325f8cd3d80bb5f22bce03eda5ba3d83693dc6 Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Mon, 6 Jul 2026 09:35:07 -0700 Subject: [PATCH 020/144] Remove reclient in favor of Siso (#2510) --- .claude/skills/migrate-to-bazelmod/SKILL.md | 2 +- .../vocabularies/TraceMachina/accept.txt | 3 +- README.md | 2 +- .../chromium-example/build_chromium_tests.sh | 4 +- kubernetes/nativelink/nativelink-config.json5 | 2 +- .../docs/content/docs/deployment/chromium.mdx | 60 +++++--------- .../docs/explanations/architecture.mdx | 2 +- web/apps/docs/content/docs/faq/caching.mdx | 2 +- web/apps/docs/content/docs/faq/cost.mdx | 2 +- .../content/docs/faq/remote-execution.mdx | 2 +- .../other-build-systems/index.mdx | 6 +- .../other-build-systems/meta.json | 2 +- .../other-build-systems/reclient.mdx | 82 ------------------- .../other-build-systems/siso.mdx | 68 +++++++++++++++ .../content/docs/getting-started/setup.mdx | 17 ++-- web/apps/docs/content/docs/index.mdx | 4 +- web/apps/docs/next.config.mjs | 5 ++ web/apps/web/app/page.tsx | 11 ++- web/apps/web/app/pricing/page.tsx | 2 +- web/apps/web/app/product/page.tsx | 6 +- .../web/components/architecture-diagram.tsx | 2 +- .../content/posts/Announcement_NativeLink.mdx | 2 +- .../Announcement_TraceMachina_Seedfunding.mdx | 2 +- .../posts/CaseStudy_ThirdwaveAutomation.mdx | 2 +- 24 files changed, 128 insertions(+), 164 deletions(-) delete mode 100644 web/apps/docs/content/docs/getting-started/other-build-systems/reclient.mdx create mode 100644 web/apps/docs/content/docs/getting-started/other-build-systems/siso.mdx diff --git a/.claude/skills/migrate-to-bazelmod/SKILL.md b/.claude/skills/migrate-to-bazelmod/SKILL.md index be40a6673..4d5cf8847 100644 --- a/.claude/skills/migrate-to-bazelmod/SKILL.md +++ b/.claude/skills/migrate-to-bazelmod/SKILL.md @@ -711,4 +711,4 @@ If a migration step in this skill doesn't fit the project (unusual repository la - Email: support@nativelink.com - Cloud: https://app.nativelink.com (zero-config remote cache plus remote execution) -NativeLink ships build cache and remote execution for Bazel/Buck2/Goma/Reclient at over a billion requests per month, and the team handles Bazel-modules migrations as part of normal customer support. +NativeLink ships build cache and remote execution for Bazel/Buck2/Goma/Siso at over a billion requests per month, and the team handles Bazel-modules migrations as part of normal customer support. diff --git a/.github/styles/config/vocabularies/TraceMachina/accept.txt b/.github/styles/config/vocabularies/TraceMachina/accept.txt index 67959137b..764b2d974 100644 --- a/.github/styles/config/vocabularies/TraceMachina/accept.txt +++ b/.github/styles/config/vocabularies/TraceMachina/accept.txt @@ -52,11 +52,10 @@ onboarding OSSF protobuf Recc -Reclient +Siso Rosetta [Rr]epository [Ss]harding -Siso SPDX Starlark Tokio diff --git a/README.md b/README.md index addcc6ff0..0469a288e 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ NativeLink is trusted in production environments to reduce costs and developer i - Utilizes remote resources to offload computational burden from local machines - Ensures consistency with a uniform, controlled build environment -NativeLink seamlessly integrates with build tools that use the Remote Execution protocol, such as [Bazel](https://bazel.build), [Buck2](https://buck2.build), [Goma](https://chromium.googlesource.com/infra/goma/client/), and [Reclient](https://github.com/bazelbuild/reclient). CMake projects work too via [`recc`](https://buildgrid.gitlab.io/recc). See [Build CMake projects with NativeLink](https://nativelink.com/docs/rbe/cmake-recc). It supports Unix-based operating systems and Windows, ensuring broad compatibility across different development environments. +NativeLink seamlessly integrates with build tools that use the Remote Execution protocol, such as [Bazel](https://bazel.build), [Buck2](https://buck2.build), [Goma](https://chromium.googlesource.com/infra/goma/client/), and [Siso](https://chromium.googlesource.com/build/+/refs/heads/main/siso/README.md). CMake projects work too via [`recc`](https://buildgrid.gitlab.io/recc). See [Build CMake projects with NativeLink](https://nativelink.com/docs/rbe/cmake-recc). It supports Unix-based operating systems and Windows, ensuring broad compatibility across different development environments. ## 🚀 Quickstart diff --git a/deploy/chromium-example/build_chromium_tests.sh b/deploy/chromium-example/build_chromium_tests.sh index 5faaabbda..1cb26d9e1 100755 --- a/deploy/chromium-example/build_chromium_tests.sh +++ b/deploy/chromium-example/build_chromium_tests.sh @@ -59,10 +59,10 @@ else fi echo "Generating ninja projects" -gn gen --args="use_remoteexec=true reclient_cfg_dir=\"../../buildtools/reclient_cfgs/linux\"" out/Default +gn gen --args="use_remoteexec=true use_siso=true" out/Default # Fetch cache and schedular IP address for passing to ninja NATIVELINK=$(kubectl get gtw nativelink-gateway -o=jsonpath='{.status.addresses[0].value}') echo "Starting autoninja build" -RBE_service=${NATIVELINK}:80 RBE_cas_service=${NATIVELINK}:80 RBE_instance='' RBE_reclient_timeout=60m RBE_exec_timeout=4m RBE_alsologtostderr=true RBE_service_no_security=true RBE_service_no_auth=true RBE_local_resource_fraction=0.00001 RBE_automatic_auth=false RBE_gcert_refresh_timeout=20 RBE_compression_threshold=-1 RBE_metrics_namespace='' RBE_platform='' RBE_experimental_credentials_helper='' RBE_experimental_credentials_helper_args='' RBE_log_http_calls=true RBE_use_rpc_credentials=false RBE_exec_strategy=remote_local_fallback autoninja -v -j 50 -C out/Default cc_unittests +SISO_REAPI_ADDRESS=${NATIVELINK}:80 SISO_REAPI_CAS_ADDRESS=${NATIVELINK}:80 SISO_REAPI_INSTANCE='' RBE_service_no_security=true autoninja -v -j 50 -C out/Default cc_unittests diff --git a/kubernetes/nativelink/nativelink-config.json5 b/kubernetes/nativelink/nativelink-config.json5 index 630d1505f..22200d092 100644 --- a/kubernetes/nativelink/nativelink-config.json5 +++ b/kubernetes/nativelink/nativelink-config.json5 @@ -94,7 +94,7 @@ { name: "MAIN_SCHEDULER", - // TODO(palfrey): use the right scheduler because reclient doesn't use the cached results? + // TODO(palfrey): use the right scheduler because siso doesn't use the cached results? // TODO(palfrey): max_bytes_per_stream simple: { supported_platform_properties: { diff --git a/web/apps/docs/content/docs/deployment/chromium.mdx b/web/apps/docs/content/docs/deployment/chromium.mdx index 98866327e..30bfd406f 100644 --- a/web/apps/docs/content/docs/deployment/chromium.mdx +++ b/web/apps/docs/content/docs/deployment/chromium.mdx @@ -1,11 +1,11 @@ --- title: Chromium -description: Building Chromium with NativeLink as the Reclient backend — a worked example for the largest public consumer. +description: Building Chromium with NativeLink as the Siso backend — a worked example for the largest public consumer. --- Chromium's build system shells out to -[Reclient](https://github.com/bazelbuild/reclient), which speaks the -Remote Execution API. Pointing it at a NativeLink cluster is a +[Siso](https://chromium.googlesource.com/build/+/refs/heads/main/siso/README.md), +which speaks the Remote Execution API. Pointing it at a NativeLink cluster is a configuration change — no patches to the Chromium tree. The Samsung Internet team runs this configuration in production for @@ -16,19 +16,17 @@ any Chromium-based browser. - A NativeLink cluster reachable from your build machines. Cache-only is fine to start; remote execution drops build times further. -- Reclient installed and on `$PATH`. Chromium's `gclient sync` will - install it under `buildtools/`. +- A Chromium checkout with `depot_tools` on `$PATH`. - The Chromium source checkout (`fetch chromium`). -## Configure Reclient +## Configure Siso -Reclient reads its server settings from environment variables. Add +Siso reads its server settings from environment variables. Add to your build shell: ```bash -export RBE_service=cas.nativelink.internal:50051 -export RBE_instance=chromium -export RBE_use_application_default_credentials=false +export SISO_REAPI_ADDRESS=cas.nativelink.internal:50051 +export SISO_REAPI_INSTANCE=chromium export RBE_service_no_security=false # mTLS in production export RBE_tls_client_auth_cert=/etc/nativelink/tls/client.crt export RBE_tls_client_auth_key=/etc/nativelink/tls/client.key @@ -38,8 +36,8 @@ export RBE_tls_ca_cert=/etc/nativelink/tls/ca.crt For local development against a non-TLS NativeLink: ```bash -export RBE_service=localhost:50051 -export RBE_instance=chromium +export SISO_REAPI_ADDRESS=localhost:50051 +export SISO_REAPI_INSTANCE=chromium export RBE_service_no_security=true ``` @@ -51,25 +49,12 @@ In your Chromium checkout: gn gen out/Default --args=' use_remoteexec=true use_goma=false - use_siso=false - reclient_cfg_dir="../../buildtools/reclient_cfgs/chromium-browser-clang" + use_siso=true ' ``` -The `use_remoteexec=true` flag is the one that matters — it tells the -build to wrap every compile invocation with Reclient. - -## Start the reproxy daemon - -Reclient uses a long-lived daemon (`reproxy`) that batches requests -to the RE-API server. Start it before invoking the build: - -```bash -buildtools/reclient/bootstrap --re_proxy=buildtools/reclient/reproxy -``` - -The daemon stays up; `bootstrap --shutdown` shuts it down when -you're done. +`autoninja` reads `args.gn`; with `use_siso=true`, it invokes +`siso ninja` for the build. ## Build @@ -83,16 +68,8 @@ of the time. ## What good looks like -The Reclient stats endpoint reports cache statistics per build: - -```bash -buildtools/reclient/reclient stats -``` - -Healthy numbers on warm builds: - - **Cache hit rate**: 85-95% on incremental builds; 60-80% on full - builds. + builds, visible from NativeLink cache metrics. - **Local fallback rate**: under 1%. Higher means workers are rejecting actions (platform mismatch, capacity). - **Network errors**: zero. Anything else is a problem. @@ -107,19 +84,20 @@ config should advertise: - `cpu_count: 8` (minimum; 16+ recommended for link steps) The -[`deployment-examples/chromium/`](https://github.com/TraceMachina/nativelink/tree/main/deployment-examples/chromium) +[`deploy/chromium-example/`](https://github.com/TraceMachina/nativelink/tree/main/deploy/chromium-example) directory in the source tree has the worker config Samsung uses. ## Troubleshooting - **All actions falling back to local.** Likely a platform-property - mismatch. Compare what Chromium is requesting (in the reclient + mismatch. Compare what Chromium is requesting (in the siso log) against what your workers advertise. +- **Siso is not being selected.** If the output directory was already + generated for Ninja, run `gn clean out/Default`, regenerate with + `use_siso=true`, and build again. - **Cache writes succeed, reads always miss.** Almost always a toolchain version mismatch between the action that wrote the cache and the one trying to read. -- **Reproxy hangs on shutdown.** Known issue — `reclient_cleanup` - fixes it. ## What's next diff --git a/web/apps/docs/content/docs/explanations/architecture.mdx b/web/apps/docs/content/docs/explanations/architecture.mdx index b2ec97080..eae6bbe12 100644 --- a/web/apps/docs/content/docs/explanations/architecture.mdx +++ b/web/apps/docs/content/docs/explanations/architecture.mdx @@ -11,7 +11,7 @@ real clusters run them together. ## The four roles {`sequenceDiagram - participant client as Client (Bazel / Buck2 / Reclient) + participant client as Client (Bazel / Buck2 / Siso) participant sched as Scheduler participant worker as Worker participant cas as CAS + AC diff --git a/web/apps/docs/content/docs/faq/caching.mdx b/web/apps/docs/content/docs/faq/caching.mdx index 4c3a897e3..c22a9772c 100644 --- a/web/apps/docs/content/docs/faq/caching.mdx +++ b/web/apps/docs/content/docs/faq/caching.mdx @@ -46,7 +46,7 @@ never touched it. You paid the cost of one HTTP request. NativeLink implements the standard [Remote Execution API](https://github.com/bazelbuild/remote-apis) — -the same protocol Bazel, Buck2, Reclient, Goma, and Pants speak. You +the same protocol Bazel, Buck2, Siso, Goma, and Pants speak. You point your build at a NativeLink server, and every action gets cached automatically. diff --git a/web/apps/docs/content/docs/faq/cost.mdx b/web/apps/docs/content/docs/faq/cost.mdx index d3b6dadc7..b4a594b97 100644 --- a/web/apps/docs/content/docs/faq/cost.mdx +++ b/web/apps/docs/content/docs/faq/cost.mdx @@ -37,7 +37,7 @@ comparison. - The full RE-API server: CAS, AC, scheduler, worker. - Every storage backend: filesystem, S3, Redis, GCS, Azure Blob. -- Every supported build client: Bazel, Buck2, Reclient, Pants, Goma. +- Every supported build client: Bazel, Buck2, Siso, Pants, Goma. - The CLI tooling and Helm chart. No artificial limits on cache size, action count, worker count, or diff --git a/web/apps/docs/content/docs/faq/remote-execution.mdx b/web/apps/docs/content/docs/faq/remote-execution.mdx index 2719c2193..2cf130d5b 100644 --- a/web/apps/docs/content/docs/faq/remote-execution.mdx +++ b/web/apps/docs/content/docs/faq/remote-execution.mdx @@ -51,7 +51,7 @@ NativeLink is a Remote Execution API server. Any build tool that speaks the protocol — [Bazel](https://bazel.build), [Buck2](https://buck2.build), -[Reclient](https://github.com/bazelbuild/reclient), +[Siso](https://chromium.googlesource.com/build/+/refs/heads/main/siso/README.md), [Pants](https://www.pantsbuild.org), [Goma](https://chromium.googlesource.com/infra/goma/client/) — plugs in with no code changes. diff --git a/web/apps/docs/content/docs/getting-started/other-build-systems/index.mdx b/web/apps/docs/content/docs/getting-started/other-build-systems/index.mdx index 1364bf34b..f550eb3b5 100644 --- a/web/apps/docs/content/docs/getting-started/other-build-systems/index.mdx +++ b/web/apps/docs/content/docs/getting-started/other-build-systems/index.mdx @@ -1,6 +1,6 @@ --- title: Other build systems -description: Use NativeLink without Bazel — Buck2, Reclient, Pants, BuildStream, and CMake with recc. +description: Use NativeLink without Bazel — Buck2, Siso, Pants, BuildStream, and CMake with recc. --- NativeLink speaks the standard Remote Execution API. If your build tool @@ -12,8 +12,8 @@ your build files. - [Buck2](/getting-started/other-build-systems/buck2) — use Buck2's built-in remote execution client with NativeLink as CAS, Action Cache, and executor. -- [Reclient](/getting-started/other-build-systems/reclient) — configure - `reproxy` and `rewrapper` for Chromium-style builds. +- [Siso](/getting-started/other-build-systems/siso) — configure + Chromium's Ninja replacement to use NativeLink through the RE API. - [Pants](/getting-started/other-build-systems/pants) — enable remote cache reads and writes from `pants.toml`. - [BuildStream](/getting-started/other-build-systems/buildstream) — point diff --git a/web/apps/docs/content/docs/getting-started/other-build-systems/meta.json b/web/apps/docs/content/docs/getting-started/other-build-systems/meta.json index 1e4ab9cf8..2f96cd11f 100644 --- a/web/apps/docs/content/docs/getting-started/other-build-systems/meta.json +++ b/web/apps/docs/content/docs/getting-started/other-build-systems/meta.json @@ -1,7 +1,7 @@ { "pages": [ "buck2", - "reclient", + "siso", "pants", "buildstream", "cmake-recc" diff --git a/web/apps/docs/content/docs/getting-started/other-build-systems/reclient.mdx b/web/apps/docs/content/docs/getting-started/other-build-systems/reclient.mdx deleted file mode 100644 index 17db1a152..000000000 --- a/web/apps/docs/content/docs/getting-started/other-build-systems/reclient.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Reclient -description: Point Reclient at NativeLink for Chromium-style remote caching and execution. ---- - -Reclient speaks the Remote Execution API through a local daemon, -`reproxy`, and command wrapper, `rewrapper`. NativeLink can be the -backend for Reclient clients such as Chromium and other GN/Ninja builds. - -For a production Chromium walkthrough, see -[Deployment → Chromium](/deployment/chromium). - -## Local cache-only setup - -Start NativeLink on `localhost:50051` with instance name `main`, then -export the environment variables Reclient reads: - -```bash -export RBE_service=localhost:50051 -export RBE_instance=main -export RBE_service_no_security=true -``` - -For cache-only compiler wrapping, run `reproxy` and invoke a command with -`rewrapper`: - -```bash -reproxy & -rewrapper -- clang++ -c hello.cc -o hello.o -``` - -For CMake projects, [`recc`](/getting-started/other-build-systems/cmake-recc) -is often the smaller setup. It uses the same Remote Execution API but fits -CMake compiler-launcher workflows directly. - -## Chromium-style setup - -Chromium checks in Reclient configs. Point them at NativeLink by enabling -remote execution in GN: - -```bash -gn gen out/Default --args=' - use_remoteexec=true - use_goma=false - use_siso=false - reclient_cfg_dir="../../buildtools/reclient_cfgs/chromium-browser-clang" -' -``` - -Start the daemon before building: - -```bash -buildtools/reclient/bootstrap --re_proxy=buildtools/reclient/reproxy -autoninja -C out/Default chrome -``` - -## Production TLS settings - -For a secured NativeLink endpoint, set the Reclient TLS variables instead -of `RBE_service_no_security=true`: - -```bash -export RBE_service=cas.nativelink.internal:50051 -export RBE_instance=main -export RBE_service_no_security=false -export RBE_tls_client_auth_cert=/etc/nativelink/tls/client.crt -export RBE_tls_client_auth_key=/etc/nativelink/tls/client.key -export RBE_tls_ca_cert=/etc/nativelink/tls/ca.crt -``` - -## Confirm it is working - -Use Reclient stats after a build: - -```bash -reclient stats -``` - -On a warm cache, cache hits should climb and network errors should stay at -zero. If actions fall back to local execution, compare the platform -properties Reclient requests with the properties your NativeLink workers -advertise. diff --git a/web/apps/docs/content/docs/getting-started/other-build-systems/siso.mdx b/web/apps/docs/content/docs/getting-started/other-build-systems/siso.mdx new file mode 100644 index 000000000..2fa2f38ab --- /dev/null +++ b/web/apps/docs/content/docs/getting-started/other-build-systems/siso.mdx @@ -0,0 +1,68 @@ +--- +title: Siso +description: Point Siso at NativeLink for Chromium-style remote caching and execution. +--- + +[Siso](https://chromium.googlesource.com/build/+/refs/heads/main/siso/README.md) +is Chromium's Ninja replacement. It runs build actions on RBE natively, +and NativeLink can be the RE API backend for Chromium and other +Siso-driven builds. + +For a production Chromium walkthrough, see +[Deployment → Chromium](/deployment/chromium). + +## Local endpoint setup + +Start NativeLink on `localhost:50051` with instance name `main`, then +export the environment variables Siso reads: + +```bash +export SISO_REAPI_ADDRESS=localhost:50051 +export SISO_REAPI_INSTANCE=main +export RBE_service_no_security=true +``` + +For CMake projects, [`recc`](/getting-started/other-build-systems/cmake-recc) +is often the smaller setup. It uses the same Remote Execution API but fits +CMake compiler-launcher workflows directly. + +## Chromium-style setup + +Chromium checks in Siso configs. Point them at NativeLink by enabling +remote execution in GN: + +```bash +gn gen out/Default --args=' + use_remoteexec=true + use_goma=false + use_siso=true +' +``` + +Build normally with `autoninja`; it detects `use_siso=true` and invokes +`siso ninja`: + +```bash +autoninja -C out/Default chrome +``` + +## Production TLS settings + +For a secured NativeLink endpoint, set the Siso TLS variables instead +of `RBE_service_no_security=true`: + +```bash +export SISO_REAPI_ADDRESS=cas.nativelink.internal:50051 +export SISO_REAPI_INSTANCE=main +export RBE_service_no_security=false +export RBE_tls_client_auth_cert=/etc/nativelink/tls/client.crt +export RBE_tls_client_auth_key=/etc/nativelink/tls/client.key +export RBE_tls_ca_cert=/etc/nativelink/tls/ca.crt +``` + +## Confirm it is working + +On a warm cache, cache hits should climb and network errors should stay at +zero. If actions fall back to local execution, compare the platform +properties Siso requests with the properties your NativeLink workers +advertise. diff --git a/web/apps/docs/content/docs/getting-started/setup.mdx b/web/apps/docs/content/docs/getting-started/setup.mdx index 43a103bea..dc5250d9e 100644 --- a/web/apps/docs/content/docs/getting-started/setup.mdx +++ b/web/apps/docs/content/docs/getting-started/setup.mdx @@ -9,7 +9,7 @@ system to the running cluster. You need a build tool that speaks the Remote Execution API — Bazel, - Buck2, Reclient, Pants, Goma, or CMake via recc. For the Docker path, + Buck2, Siso, Pants, Goma, or CMake via recc. For the Docker path, Docker 24+. For the Nix path, a recent Nix install with flakes enabled. @@ -93,7 +93,7 @@ the connection succeeding does. ## Point your build system at it - + @@ -138,20 +138,17 @@ buck2 build //... --remote-cache - + -Set the environment variables Reclient reads: +Set the endpoint variables Siso reads: ```bash -export RBE_service=localhost:50051 -export RBE_instance=main +export SISO_REAPI_ADDRESS=localhost:50051 +export SISO_REAPI_INSTANCE=main export RBE_service_no_security=true # only for local TLS-free dev - -reproxy & -rewrapper -- ``` -The Chromium build is the canonical Reclient consumer; see +The Chromium build is the canonical Siso consumer; see [Deployment → Chromium](/deployment/chromium) for a full example. diff --git a/web/apps/docs/content/docs/index.mdx b/web/apps/docs/content/docs/index.mdx index 390dec76d..e62b50b26 100644 --- a/web/apps/docs/content/docs/index.mdx +++ b/web/apps/docs/content/docs/index.mdx @@ -4,7 +4,7 @@ description: NativeLink is a high-performance remote build cache and execution p --- NativeLink is a high-performance remote build cache and execution platform for -build systems that speak the Remote Execution API — **Bazel, Buck2, Reclient, +build systems that speak the Remote Execution API — **Bazel, Buck2, Siso, Pants, Goma**, and **CMake via recc**. It's open source, written in Rust, and battle-tested on over a billion build requests a month. @@ -31,7 +31,7 @@ serves its job well. - [NativeLink on-prem](/getting-started/on-prem) — a single-server deployment that survives a team-wide rollout. - [Other build systems](/getting-started/other-build-systems) — using NativeLink - without Bazel (Buck2, Reclient, Pants, CMake). + without Bazel (Buck2, Siso, Pants, CMake). ### I want to configure or operate it diff --git a/web/apps/docs/next.config.mjs b/web/apps/docs/next.config.mjs index 9b89fe643..2fa43e350 100644 --- a/web/apps/docs/next.config.mjs +++ b/web/apps/docs/next.config.mjs @@ -15,6 +15,11 @@ const nextConfig = { destination: "/configuration/production", permanent: true, }, + { + source: "/getting-started/other-build-systems/reclient", + destination: "/getting-started/other-build-systems/siso", + permanent: true, + }, ]; }, }; diff --git a/web/apps/web/app/page.tsx b/web/apps/web/app/page.tsx index eb6763935..afd270f5c 100644 --- a/web/apps/web/app/page.tsx +++ b/web/apps/web/app/page.tsx @@ -16,11 +16,10 @@ export const metadata = { const integrations = [ "Bazel", "Buck2", - "Reclient", + "Siso", "Buildstream", "recc", "GN", - "Siso", "Pants", "Goma", "CMake", @@ -86,7 +85,7 @@ const stats = [ { value: "0", label: "build-system rewrites", - body: "required for Bazel, Buck2, Reclient, Goma, or CMake via recc or BuildStream.", + body: "required for Bazel, Buck2, Siso, Goma, or CMake via recc or BuildStream.", }, ]; @@ -113,11 +112,11 @@ const benefits = [ }, { title: "Ten minutes to your first cache hit.", - body: "One Docker command. Drops into your existing Bazel, Buck2, Reclient, or CMake setup with zero rewrites.", + body: "One Docker command. Drops into your existing Bazel, Buck2, Siso, or CMake setup with zero rewrites.", }, { title: "Works with what you've got.", - body: "C++, Rust, Python, Go, and more. Bazel, Buck2, Reclient, CMake. AWS, GCP, Azure, or your own hardware. No lock-in.", + body: "C++, Rust, Python, Go, and more. Bazel, Buck2, Siso, CMake. AWS, GCP, Azure, or your own hardware. No lock-in.", }, ]; @@ -144,7 +143,7 @@ const industries = [ }, { title: "Browsers & Web Platforms", - body: "Chromium and its descendants are some of the largest C++ codebases on the open web. NativeLink speaks reclient natively.", + body: "Chromium and its descendants are some of the largest C++ codebases on the open web. NativeLink speaks Siso natively.", }, ]; diff --git a/web/apps/web/app/pricing/page.tsx b/web/apps/web/app/pricing/page.tsx index 78e395001..0a099131d 100644 --- a/web/apps/web/app/pricing/page.tsx +++ b/web/apps/web/app/pricing/page.tsx @@ -13,7 +13,7 @@ const tiers = [ "Self-hosted", "Community support on Slack", "Distributed scheduler & remote caching", - "All build systems (Bazel, Buck2, Reclient, Pants)", + "All build systems (Bazel, Buck2, Siso, Pants)", "All major cloud providers", ], cta: { label: "Get started", href: "/docs" }, diff --git a/web/apps/web/app/product/page.tsx b/web/apps/web/app/product/page.tsx index b8e5b947b..6b980e76a 100644 --- a/web/apps/web/app/product/page.tsx +++ b/web/apps/web/app/product/page.tsx @@ -15,7 +15,7 @@ const pillars = [ { eyebrow: "Remote cache", title: "Cache once. Reuse forever.", - body: "Content-addressable storage deduplicates every artifact your team produces. If a teammate, your CI, or an agent has already built it, you get it back in milliseconds. Drops into Bazel, Buck2, Reclient, Pants, Goma — and CMake via recc.", + body: "Content-addressable storage deduplicates every artifact your team produces. If a teammate, your CI, or an agent has already built it, you get it back in milliseconds. Drops into Bazel, Buck2, Siso, Pants, Goma — and CMake via recc.", metric: "1B+", metricLabel: "requests / month", icon: ( @@ -62,7 +62,7 @@ const integrationGroups = [ { label: "Languages", items: ["C++", "Rust", "Python", "Go", "Java", "Kotlin", "Swift"] }, { label: "Build systems", - items: ["Bazel", "Buck2", "Reclient", "Soong", "Pants", "Goma", "CMake (recc)"], + items: ["Bazel", "Buck2", "Siso", "Soong", "Pants", "Goma", "CMake (recc)"], }, { label: "Cloud", items: ["AWS", "GCP", "Azure", "Bare metal"] }, { label: "CI", items: ["GitHub Actions", "GitLab", "Buildkite", "Jenkins"] }, @@ -105,7 +105,7 @@ const faqItems: FAQItem[] = [ }, { q: "Does it work with my existing tools?", - a: "Yes. Anything that speaks the Remote Execution API — Bazel, Buck2, Reclient, Goma, Pants, or CMake via recc — works without modification.", + a: "Yes. Anything that speaks the Remote Execution API — Bazel, Buck2, Siso, Goma, Pants, or CMake via recc — works without modification.", }, { q: "How do I keep my Bazel setup hermetic?", diff --git a/web/apps/web/components/architecture-diagram.tsx b/web/apps/web/components/architecture-diagram.tsx index 0002e592b..c6c867a66 100644 --- a/web/apps/web/components/architecture-diagram.tsx +++ b/web/apps/web/components/architecture-diagram.tsx @@ -100,7 +100,7 @@ export function ArchitectureDiagram() { fontSize="10" fill="rgb(var(--nl-color-muted))" > - Reclient · Pants + Siso · Pants diff --git a/web/apps/web/content/posts/Announcement_NativeLink.mdx b/web/apps/web/content/posts/Announcement_NativeLink.mdx index 1de14f928..43c8f24bf 100644 --- a/web/apps/web/content/posts/Announcement_NativeLink.mdx +++ b/web/apps/web/content/posts/Announcement_NativeLink.mdx @@ -9,7 +9,7 @@ pubDate: 2024-09-17 Today, we're excited to release NativeLink–a Rust implementation of Bazel's Remote Build Execution protocol (RBE) and Content Addressable Storage (CAS) designed to run your code and get out of the way. NativeLink seamlessly -integrates with Bazel, Reclient, Goma, and Buck2. It comes at no cost and +integrates with Bazel, Siso, Goma, and Buck2. It comes at no cost and works on every major operating system. diff --git a/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx b/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx index c794bff5d..872b731d6 100644 --- a/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx +++ b/web/apps/web/content/posts/Announcement_TraceMachina_Seedfunding.mdx @@ -54,7 +54,7 @@ Rust-based simulation infrastructure platform designed to provide an advanced simulation environment for technologies where safety is paramount. -NativeLink is the only platform for Bazel, Buck2, and Reclient written in +NativeLink is the only platform for Bazel, Buck2, and Siso written in native code, tailored to handle large objects and intricate systems, across native and interpreted programming languages. diff --git a/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx b/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx index e0e197a12..2ed534f08 100644 --- a/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx +++ b/web/apps/web/content/posts/CaseStudy_ThirdwaveAutomation.mdx @@ -65,7 +65,7 @@ Open and extensible platform built on Bazel Licensed as Apache 2.0 open source, NativeLink seamlessly integrates with Bazel, the modern build client developed by Google and now used by 1,000+ organizations including Tesla, Nvidia, Ford, Stripe, Dropbox, Datadog, and Databricks. -NativeLink works with all client-side build tools that support the Remote Bazel Execution (RBE) protocol such as Bazel, Buck2, Pantsbuild, and Reclient. +NativeLink works with all client-side build tools that support the Remote Bazel Execution (RBE) protocol such as Bazel, Buck2, Pantsbuild, and Siso. You can customize NativeLink via JSON or YAML, as well as via Starlark, a Python-based declarative language for configuring Bazel with custom build rules and macros for specific projects and platforms. From 884ffdea523ad4ecb00e01c94a7d73cd86edf09a Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Tue, 7 Jul 2026 00:28:14 +0530 Subject: [PATCH 021/144] Migrate Azure Blob store to use the Azure v1.0 crates (#2472) --- Cargo.lock | 536 ++++-------- MODULE.bazel.lock | 62 +- nativelink-config/src/stores.rs | 29 +- nativelink-error/Cargo.toml | 2 +- nativelink-store/BUILD.bazel | 9 +- nativelink-store/Cargo.toml | 32 +- nativelink-store/src/azure_blob_store.rs | 824 +++++++----------- .../tests/azure_blob_store_test.rs | 802 ++++++++--------- .../tests/mongo_runner/downloader.rs | 6 + nativelink-test/fuzz/Cargo.lock | 19 +- .../docs/reference/nativelink-config/main.mdx | 10 +- 11 files changed, 910 insertions(+), 1421 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fe033363..3b35de285 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "RustyXML" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5ace29ee3216de37c0546865ad08edef58b0f9e76838ed8959a84a990e58c5" - [[package]] name = "adler2" version = "2.0.1" @@ -142,27 +136,38 @@ dependencies = [ ] [[package]] -name = "async-channel" -version = "1.9.0" +name = "async-lock" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", + "event-listener", + "event-listener-strategy", + "pin-project-lite", ] [[package]] -name = "async-lock" -version = "3.4.1" +name = "async-stream" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", + "async-stream-impl", + "futures-core", "pin-project-lite", ] +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -214,7 +219,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand", "hex", "http 1.4.1", "ring", @@ -254,7 +259,7 @@ dependencies = [ "aws-types", "bytes", "bytes-utils", - "fastrand 2.4.1", + "fastrand", "http 0.2.12", "http 1.4.1", "http-body 0.4.6", @@ -286,7 +291,7 @@ dependencies = [ "aws-smithy-xml", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand", "hex", "hmac", "http 0.2.12", @@ -317,7 +322,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand", "http 0.2.12", "http 1.4.1", "regex-lite", @@ -341,7 +346,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand", "http 0.2.12", "http 1.4.1", "regex-lite", @@ -366,7 +371,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "fastrand 2.4.1", + "fastrand", "http 0.2.12", "http 1.4.1", "regex-lite", @@ -543,7 +548,7 @@ dependencies = [ "aws-smithy-schema", "aws-smithy-types", "bytes", - "fastrand 2.4.1", + "fastrand", "http 0.2.12", "http 1.4.1", "http-body 0.4.6", @@ -689,87 +694,73 @@ dependencies = [ [[package]] name = "azure_core" -version = "0.21.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b552ad43a45a746461ec3d3a51dfb6466b4759209414b439c165eb6a6b7729e" +checksum = "fe6a26a7d374b440015cbbcbf2d9d8be5a133aa940599f5e5dc569504baa262e" dependencies = [ + "async-lock", "async-trait", - "base64 0.22.1", + "azure_core_macros", "bytes", - "dyn-clone", "futures", - "getrandom 0.2.16", - "hmac", - "http-types", - "once_cell", - "paste", "pin-project", - "quick-xml", - "rand 0.8.6", "rustc_version", "serde", "serde_json", - "sha2", - "time", + "tokio", "tracing", - "url", - "uuid", + "typespec", + "typespec_client_core", ] [[package]] -name = "azure_storage" -version = "0.21.0" +name = "azure_core_macros" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f838159f4d29cb400a14d9d757578ba495ae64feb07a7516bf9e4415127126" +checksum = "b9b52dba6a345f3ad2d42ff8d0d63df9d0994cfa29657bf18ffdbf149f78a4f5" dependencies = [ - "RustyXML", - "async-lock", - "async-trait", - "azure_core", - "bytes", - "serde", - "serde_derive", - "time", + "proc-macro2", + "quote", + "syn", "tracing", - "url", - "uuid", ] [[package]] -name = "azure_storage_blobs" -version = "0.21.0" +name = "azure_identity" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97e83c3636ae86d9a6a7962b2112e3b19eb3903915c50ce06ff54ff0a2e6a7e4" +checksum = "32edf96b356ca7c51d7590c4925cc36efc3947a5da4468e8e0b25c56ecbb3de5" dependencies = [ - "RustyXML", + "async-lock", + "async-trait", "azure_core", - "azure_storage", - "azure_svc_blobstorage", - "bytes", "futures", + "pin-project", "serde", - "serde_derive", "serde_json", "time", + "tokio", "tracing", "url", - "uuid", ] [[package]] -name = "azure_svc_blobstorage" -version = "0.21.0" +name = "azure_storage_blob" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6c6f20c5611b885ba94c7bae5e02849a267381aecb8aee577e8c35ff4064c6" +checksum = "1756febbcca86c862ef718b983b505d08bd65a9bc984a915b0a16af4a4c3fe5b" dependencies = [ + "async-stream", + "async-trait", "azure_core", "bytes", "futures", - "log", - "once_cell", + "percent-encoding", + "pin-project", "serde", "serde_json", "time", + "tokio", ] [[package]] @@ -778,7 +769,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ - "fastrand 2.4.1", + "fastrand", ] [[package]] @@ -981,12 +972,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chacha20" version = "0.10.0" @@ -1612,12 +1597,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -1635,19 +1614,10 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -1803,21 +1773,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -1860,16 +1815,16 @@ dependencies = [ [[package]] name = "gcloud-auth" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bdedbc36e6b9d8d79558fbf2ebc098745bc721e9d37d3e369558e420038e360" +checksum = "b43924e3df02cb3b846ca66a7ee58e8c13eb2556d0308c71f6154083f6980365" dependencies = [ "async-trait", "base64 0.22.1", "gcloud-metadata", "home", "jsonwebtoken", - "reqwest 0.12.24", + "reqwest", "serde", "serde_json", "thiserror 2.0.18", @@ -1886,16 +1841,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd3152612316be627be52fe9ca72331eb48425059b3a6a700e7adde223e061d5" dependencies = [ - "reqwest 0.13.4", + "reqwest", "thiserror 2.0.18", "tokio", ] [[package]] name = "gcloud-storage" -version = "1.1.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3515c85ca8d12aaf1104c9765f46d91a9ddd2a62b853fe12db109a40cde06e1" +checksum = "e17f9662a6966402de91daf0edb5accaae05c87f1a85479e57b95d2af7284b9f" dependencies = [ "anyhow", "base64 0.22.1", @@ -1908,7 +1863,7 @@ dependencies = [ "percent-encoding", "pkcs8", "regex", - "reqwest 0.12.24", + "reqwest", "reqwest-middleware", "ring", "serde", @@ -1933,17 +1888,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -1953,7 +1897,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -2238,26 +2182,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-types" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel", - "base64 0.13.1", - "futures-lite", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", -] - [[package]] name = "httparse" version = "1.10.1" @@ -2315,7 +2239,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.3", ] [[package]] @@ -2349,7 +2272,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2517,21 +2440,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "io-lifetimes" version = "2.0.4" @@ -2664,11 +2572,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -2779,12 +2688,6 @@ dependencies = [ "hashbrown 0.16.1", ] -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "lz4_flex" version = "0.11.6" @@ -2937,7 +2840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "windows-sys 0.61.2", ] @@ -3109,7 +3012,7 @@ dependencies = [ "prost", "prost-types", "redis", - "reqwest 0.12.24", + "reqwest", "rustls-pki-types", "serde", "serde_json5", @@ -3266,8 +3169,8 @@ dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "azure_core", - "azure_storage", - "azure_storage_blobs", + "azure_identity", + "azure_storage_blob", "base64 0.22.1", "blake3", "byteorder", @@ -3309,7 +3212,7 @@ dependencies = [ "redis", "redis-test", "regex", - "reqwest 0.12.24", + "reqwest", "reqwest-middleware", "rlimit", "rustls", @@ -3327,6 +3230,7 @@ dependencies = [ "tracing-test", "url", "uuid", + "webpki-roots 1.0.3", "wincode", "zip", ] @@ -3710,12 +3614,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pastey" version = "0.2.2" @@ -4016,69 +3914,14 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", "serde", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2 0.6.4", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.4", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.45" @@ -4106,19 +3949,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.6" @@ -4151,16 +3981,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -4181,15 +4001,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -4214,15 +4025,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "redis" version = "1.0.0" @@ -4366,15 +4168,16 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.24" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", + "h2", "http 1.4.1", "http-body 1.0.1", "http-body-util", @@ -4387,9 +4190,9 @@ dependencies = [ "mime_guess", "percent-encoding", "pin-project-lite", - "quinn", "rustls", "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "serde_urlencoded", @@ -4405,50 +4208,20 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.3", -] - -[[package]] -name = "reqwest" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "http 1.4.1", - "http-body 1.0.1", - "http-body-util", - "hyper", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", ] [[package]] name = "reqwest-middleware" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +checksum = "07bc3f1384cffa4f274dad2d4ddd73aed32fed8f786d96c6be8aa4e5fd3c3b58" dependencies = [ "anyhow", "async-trait", "http 1.4.1", - "reqwest 0.12.24", + "reqwest", "serde", - "thiserror 1.0.69", + "thiserror 2.0.18", "tower-service", ] @@ -4530,12 +4303,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustc_version" version = "0.4.1" @@ -4601,7 +4368,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ - "web-time", "zeroize", ] @@ -4856,17 +4622,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror 1.0.69", -] - [[package]] name = "serde_test" version = "1.0.177" @@ -5221,7 +4976,7 @@ version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "fastrand 2.4.1", + "fastrand", "getrandom 0.3.4", "once_cell", "rustix", @@ -5285,7 +5040,6 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", - "js-sys", "num-conv", "powerfmt", "serde_core", @@ -5695,6 +5449,58 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "typespec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21666a31293beab8f41d38c2849ddbc342cd9c7cb4d71a9818868287a8934e53" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "quick-xml", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "typespec_client_core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924f0c734e0ac3b881ab99d032bd28fcc969d2bb73ef1b8dd4772fd8e518a382" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "dyn-clone", + "futures", + "pin-project", + "rand 0.10.1", + "reqwest", + "serde", + "serde_json", + "time", + "tokio", + "tracing", + "typespec", + "typespec_macros", + "url", + "uuid", +] + +[[package]] +name = "typespec_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c608f4427943f8adb211abc95c87672b1b98847152783507d54e3246e502f60" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "ucd-trie" version = "0.1.7" @@ -5756,7 +5562,6 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", ] [[package]] @@ -5814,12 +5619,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -5839,12 +5638,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -5871,9 +5664,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -5882,38 +5675,21 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5921,22 +5697,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] @@ -5965,9 +5741,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -5990,9 +5766,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index cf67c3255..5f75317fe 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -770,7 +770,6 @@ } }, "@@rules_rs+//rs:extensions.bzl%crate": { - "RustyXML_0.3.0": "{\"dependencies\":[],\"features\":{\"bench\":[]}}", "adler2_2.0.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"std\"],\"rustc-dep-of-std\":[\"core\"],\"std\":[]}}", "ahash_0.8.12": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"const-random\",\"optional\":true,\"req\":\"^0.1.17\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.2\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"hashbrown\",\"req\":\"^0.14.3\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"no-panic\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"once_cell\",\"req\":\"^1.18.0\",\"target\":\"cfg(not(all(target_arch = \\\"arm\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"pcg-mwc\",\"req\":\"^0.2.1\"},{\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"seahash\",\"req\":\"^4.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.117\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.59\"},{\"kind\":\"dev\",\"name\":\"smallvec\",\"req\":\"^1.13.1\"},{\"kind\":\"build\",\"name\":\"version_check\",\"req\":\"^0.9.4\"},{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"zerocopy\",\"req\":\"^0.8.24\"}],\"features\":{\"atomic-polyfill\":[\"dep:portable-atomic\",\"once_cell/critical-section\"],\"compile-time-rng\":[\"const-random\"],\"default\":[\"std\",\"runtime-rng\"],\"nightly-arm-aes\":[],\"no-rng\":[],\"runtime-rng\":[\"getrandom\"],\"std\":[]}}", "aho-corasick_1.1.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.4.0\"}],\"features\":{\"default\":[\"std\",\"perf-literal\"],\"logging\":[\"dep:log\"],\"perf-literal\":[\"dep:memchr\"],\"std\":[\"memchr?/std\"]}}", @@ -793,8 +792,9 @@ "arrayref_0.3.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"}],\"features\":{}}", "arrayvec_0.7.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.4\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"matches\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.4\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "assert-json-diff_2.0.2": "{\"dependencies\":[{\"name\":\"serde\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.8\"}],\"features\":{}}", - "async-channel_1.9.0": "{\"dependencies\":[{\"name\":\"concurrent-queue\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3\"},{\"name\":\"event-listener\",\"req\":\"^2.4.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.5\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^1\"}],\"features\":{}}", "async-lock_3.4.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"default_features\":false,\"name\":\"event-listener-strategy\",\"req\":\"^0.5.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"flume\",\"req\":\"^0.11.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"event-listener/loom\",\"dep:loom\"],\"std\":[\"event-listener/std\",\"event-listener-strategy/std\"]}}", + "async-stream-impl_0.3.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0.2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{}}", + "async-stream_0.3.6": "{\"dependencies\":[{\"name\":\"async-stream-impl\",\"req\":\"=0.3.6\"},{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"}],\"features\":{}}", "async-trait_0.1.89": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.30\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"full\",\"parsing\",\"printing\",\"proc-macro\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.40\"},{\"kind\":\"dev\",\"name\":\"tracing-attributes\",\"req\":\"^0.1.27\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.81\"}],\"features\":{}}", "atomic-waker_1.1.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.7.0\"}],\"features\":{}}", "atomic_0.6.1": "{\"dependencies\":[{\"name\":\"bytemuck\",\"req\":\"^1.13.1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.219\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.219\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"}],\"features\":{\"default\":[\"fallback\"],\"fallback\":[],\"nightly\":[],\"serde\":[\"dep:serde\"],\"std\":[]}}", @@ -827,10 +827,10 @@ "axum-core_0.5.6": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.0.0\"},{\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.0\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.25.0\"},{\"features\":[\"limit\"],\"name\":\"tower-http\",\"optional\":true,\"req\":\"^0.6.0\"},{\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower-http\",\"req\":\"^0.6.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.37\"}],\"features\":{\"__private_docs\":[\"dep:tower-http\"],\"tracing\":[\"dep:tracing\"]}}", "axum_0.8.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"axum-core\",\"req\":\"^0.5.5\"},{\"name\":\"axum-macros\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"form_urlencoded\",\"optional\":true,\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"hyper\",\"optional\":true,\"req\":\"^1.1.0\"},{\"features\":[\"client\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\"},{\"features\":[\"tokio\",\"server\",\"service\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.3\"},{\"name\":\"itoa\",\"req\":\"^1.0.5\"},{\"name\":\"matchit\",\"req\":\"=0.8.4\"},{\"name\":\"memchr\",\"req\":\"^2.4.1\"},{\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"name\":\"multer\",\"optional\":true,\"req\":\"^3.0.0\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.211\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.221\"},{\"name\":\"serde_core\",\"req\":\"^1.0.221\"},{\"features\":[\"raw_value\"],\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"raw_value\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"serde_path_to_error\",\"optional\":true,\"req\":\"^0.1.8\"},{\"name\":\"serde_urlencoded\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.0\"},{\"features\":[\"serde-human-readable\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\"},{\"features\":[\"time\"],\"name\":\"tokio\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^1.44\"},{\"features\":[\"macros\",\"rt\",\"rt-multi-thread\",\"net\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"package\":\"tokio\",\"req\":\"^1.44.2\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"name\":\"tokio-tungstenite\",\"optional\":true,\"req\":\"^0.28.0\"},{\"kind\":\"dev\",\"name\":\"tokio-tungstenite\",\"req\":\"^0.28.0\"},{\"default_features\":false,\"features\":[\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"features\":[\"util\",\"timeout\",\"limit\",\"load-shed\",\"steer\",\"filter\"],\"kind\":\"dev\",\"name\":\"tower\",\"package\":\"tower\",\"req\":\"^0.5.2\"},{\"features\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"cors\",\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"name\":\"tower-http\",\"optional\":true,\"req\":\"^0.6.0\"},{\"features\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"cors\",\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"kind\":\"dev\",\"name\":\"tower-http\",\"req\":\"^0.6.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.2\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"features\":[\"serde\",\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"__private\":[\"tokio\",\"http1\",\"dep:reqwest\"],\"__private_docs\":[\"axum-core/__private_docs\",\"tower/full\",\"dep:serde\",\"dep:tower-http\"],\"default\":[\"form\",\"http1\",\"json\",\"matched-path\",\"original-uri\",\"query\",\"tokio\",\"tower-log\",\"tracing\"],\"form\":[\"dep:form_urlencoded\",\"dep:serde_urlencoded\",\"dep:serde_path_to_error\"],\"http1\":[\"dep:hyper\",\"hyper?/http1\",\"hyper-util?/http1\"],\"http2\":[\"dep:hyper\",\"hyper?/http2\",\"hyper-util?/http2\"],\"json\":[\"dep:serde_json\",\"dep:serde_path_to_error\"],\"macros\":[\"dep:axum-macros\"],\"matched-path\":[],\"multipart\":[\"dep:multer\"],\"original-uri\":[],\"query\":[\"dep:form_urlencoded\",\"dep:serde_urlencoded\",\"dep:serde_path_to_error\"],\"tokio\":[\"dep:hyper-util\",\"dep:tokio\",\"tokio/net\",\"tokio/rt\",\"tower/make\",\"tokio/macros\"],\"tower-log\":[\"tower/log\"],\"tracing\":[\"dep:tracing\",\"axum-core/tracing\"],\"ws\":[\"dep:hyper\",\"tokio\",\"dep:tokio-tungstenite\",\"dep:sha1\",\"dep:base64\"]}}", "axum_0.8.8": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"axum-core\",\"req\":\"^0.5.5\"},{\"name\":\"axum-macros\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"form_urlencoded\",\"optional\":true,\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"hyper\",\"optional\":true,\"req\":\"^1.1.0\"},{\"features\":[\"client\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\"},{\"features\":[\"tokio\",\"server\",\"service\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.3\"},{\"name\":\"itoa\",\"req\":\"^1.0.5\"},{\"name\":\"matchit\",\"req\":\"=0.8.4\"},{\"name\":\"memchr\",\"req\":\"^2.4.1\"},{\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"name\":\"multer\",\"optional\":true,\"req\":\"^3.0.0\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.211\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.221\"},{\"name\":\"serde_core\",\"req\":\"^1.0.221\"},{\"features\":[\"raw_value\"],\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"raw_value\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"serde_path_to_error\",\"optional\":true,\"req\":\"^0.1.8\"},{\"name\":\"serde_urlencoded\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.0\"},{\"features\":[\"serde-human-readable\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\"},{\"features\":[\"time\"],\"name\":\"tokio\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^1.44\"},{\"features\":[\"macros\",\"rt\",\"rt-multi-thread\",\"net\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"package\":\"tokio\",\"req\":\"^1.44.2\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"name\":\"tokio-tungstenite\",\"optional\":true,\"req\":\"^0.28.0\"},{\"kind\":\"dev\",\"name\":\"tokio-tungstenite\",\"req\":\"^0.28.0\"},{\"default_features\":false,\"features\":[\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"features\":[\"util\",\"timeout\",\"limit\",\"load-shed\",\"steer\",\"filter\"],\"kind\":\"dev\",\"name\":\"tower\",\"package\":\"tower\",\"req\":\"^0.5.2\"},{\"features\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"cors\",\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"name\":\"tower-http\",\"optional\":true,\"req\":\"^0.6.0\"},{\"features\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"cors\",\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"kind\":\"dev\",\"name\":\"tower-http\",\"req\":\"^0.6.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.2\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"features\":[\"serde\",\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"__private\":[\"tokio\",\"http1\",\"dep:reqwest\"],\"__private_docs\":[\"axum-core/__private_docs\",\"tower/full\",\"dep:serde\",\"dep:tower-http\"],\"default\":[\"form\",\"http1\",\"json\",\"matched-path\",\"original-uri\",\"query\",\"tokio\",\"tower-log\",\"tracing\"],\"form\":[\"dep:form_urlencoded\",\"dep:serde_urlencoded\",\"dep:serde_path_to_error\"],\"http1\":[\"dep:hyper\",\"hyper?/http1\",\"hyper-util?/http1\"],\"http2\":[\"dep:hyper\",\"hyper?/http2\",\"hyper-util?/http2\"],\"json\":[\"dep:serde_json\",\"dep:serde_path_to_error\"],\"macros\":[\"dep:axum-macros\"],\"matched-path\":[],\"multipart\":[\"dep:multer\"],\"original-uri\":[],\"query\":[\"dep:form_urlencoded\",\"dep:serde_urlencoded\",\"dep:serde_path_to_error\"],\"tokio\":[\"dep:hyper-util\",\"dep:tokio\",\"tokio/net\",\"tokio/rt\",\"tower/make\",\"tokio/macros\"],\"tower-log\":[\"tower/log\"],\"tracing\":[\"dep:tracing\",\"axum-core/tracing\"],\"ws\":[\"dep:hyper\",\"tokio\",\"dep:tokio-tungstenite\",\"dep:sha1\",\"dep:base64\"]}}", - "azure_core_0.21.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"features\":[\"js\"],\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"name\":\"http-types\",\"req\":\"^2.12\"},{\"name\":\"once_cell\",\"req\":\"^1.18\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"paste\",\"req\":\"^1.0\"},{\"name\":\"pin-project\",\"req\":\"^1.0\"},{\"features\":[\"serialize\",\"serde-types\"],\"name\":\"quick-xml\",\"optional\":true,\"req\":\"^0.31\"},{\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12.0\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"serde-well-known\",\"macros\"],\"name\":\"time\",\"req\":\"^0.3.10\"},{\"features\":[\"wasm-bindgen\"],\"name\":\"time\",\"req\":\"^0.3.10\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"default\",\"macros\",\"rt\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"name\":\"tracing\",\"req\":\"^0.1.40\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"req\":\"^2.2\"},{\"name\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"azurite_workaround\":[],\"default\":[],\"enable_reqwest\":[\"reqwest/default-tls\"],\"enable_reqwest_gzip\":[\"reqwest/gzip\"],\"enable_reqwest_rustls\":[\"reqwest/rustls-tls\"],\"hmac_openssl\":[\"dep:openssl\"],\"hmac_rust\":[\"dep:sha2\",\"dep:hmac\"],\"test_e2e\":[],\"tokio-fs\":[\"tokio/fs\",\"tokio/sync\",\"tokio/io-util\"],\"tokio-sleep\":[\"tokio\"],\"xml\":[\"quick-xml\"]}}", - "azure_storage_0.21.0": "{\"dependencies\":[{\"name\":\"RustyXML\",\"req\":\"^0.3\"},{\"name\":\"async-lock\",\"req\":\"^3.1\"},{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"features\":[\"xml\"],\"name\":\"azure_core\",\"req\":\"^0.21\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"name\":\"time\",\"req\":\"^0.3.10\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"name\":\"tracing\",\"req\":\"^0.1.40\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"req\":\"^2.2\"},{\"features\":[\"v4\",\"serde\"],\"name\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"enable_reqwest\",\"hmac_rust\"],\"enable_reqwest\":[\"azure_core/enable_reqwest\"],\"enable_reqwest_rustls\":[\"azure_core/enable_reqwest_rustls\"],\"hmac_openssl\":[\"azure_core/hmac_openssl\"],\"hmac_rust\":[\"azure_core/hmac_rust\"],\"test_e2e\":[],\"test_integration\":[]}}", - "azure_storage_blobs_0.21.0": "{\"dependencies\":[{\"name\":\"RustyXML\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"features\":[\"xml\"],\"name\":\"azure_core\",\"req\":\"^0.21\"},{\"features\":[\"tokio-fs\"],\"kind\":\"dev\",\"name\":\"azure_core\",\"req\":\"^0.21\"},{\"default_features\":false,\"name\":\"azure_storage\",\"req\":\"^0.21\"},{\"default_features\":false,\"features\":[\"default_tag\"],\"name\":\"azure_svc_blobstorage\",\"req\":\"^0.21\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"features\":[\"derive\",\"env\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.0\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"md5\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"md5\",\"req\":\"^0.7\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"time\",\"req\":\"^0.3.10\"},{\"features\":[\"macros\",\"rt-multi-thread\",\"io-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"name\":\"tracing\",\"req\":\"^0.1.40\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"req\":\"^2.2\"},{\"features\":[\"v4\",\"serde\"],\"name\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"azurite_workaround\":[\"azure_core/azurite_workaround\"],\"default\":[\"enable_reqwest\",\"hmac_rust\"],\"enable_reqwest\":[\"azure_core/enable_reqwest\",\"azure_storage/enable_reqwest\",\"azure_svc_blobstorage/enable_reqwest\"],\"enable_reqwest_rustls\":[\"azure_core/enable_reqwest_rustls\",\"azure_storage/enable_reqwest_rustls\",\"azure_svc_blobstorage/enable_reqwest_rustls\"],\"hmac_openssl\":[\"azure_core/hmac_openssl\"],\"hmac_rust\":[\"azure_core/hmac_rust\"],\"md5\":[\"dep:md5\"],\"test_e2e\":[],\"test_integration\":[]}}", - "azure_svc_blobstorage_0.21.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"features\":[\"xml\"],\"name\":\"azure_core\",\"req\":\"^0.21\"},{\"name\":\"bytes\",\"req\":\"^1.3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1.18\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"time\",\"req\":\"^0.3\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.23\"}],\"features\":{\"default\":[\"default_tag\",\"enable_reqwest\"],\"default_tag\":[\"package-2021-12\"],\"enable_reqwest\":[\"azure_core/enable_reqwest\"],\"enable_reqwest_rustls\":[\"azure_core/enable_reqwest_rustls\"],\"package-2021-02\":[],\"package-2021-04\":[],\"package-2021-08\":[],\"package-2021-12\":[],\"package-2021-12-preview\":[]}}", + "azure_core_1.0.0": "{\"dependencies\":[{\"name\":\"async-lock\",\"req\":\"^3.4\"},{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"azure_core_macros\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"azure_core_macros\",\"req\":\"^1.0.0\"},{\"name\":\"bytes\",\"req\":\"^1.11.1\"},{\"features\":[\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^1.4.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"include-file\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"json-patch\",\"req\":\"^4.1.0\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.79\"},{\"name\":\"pin-project\",\"req\":\"^1.1\"},{\"default_features\":false,\"features\":[\"stream\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.13.2\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0.149\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"macros\",\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.49\"},{\"default_features\":false,\"features\":[\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.49\"},{\"name\":\"tracing\",\"req\":\"^0.1.44\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"http\",\"json\"],\"name\":\"typespec\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"derive\",\"http\",\"json\"],\"name\":\"typespec_client_core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"gzip\",\"native-tls\"],\"kind\":\"dev\",\"name\":\"ureq\",\"req\":\"^3.2.0\"}],\"features\":{\"debug\":[\"typespec_client_core/debug\"],\"decimal\":[\"typespec_client_core/decimal\"],\"default\":[\"reqwest\",\"reqwest_deflate\",\"reqwest_gzip\",\"reqwest_rustls\",\"tokio\"],\"hmac_openssl\":[\"dep:openssl\"],\"hmac_rust\":[\"dep:sha2\",\"dep:hmac\"],\"reqwest\":[\"typespec_client_core/reqwest\"],\"reqwest_deflate\":[\"reqwest\",\"typespec_client_core/reqwest_deflate\"],\"reqwest_gzip\":[\"reqwest\",\"typespec_client_core/reqwest_gzip\"],\"reqwest_rustls\":[\"reqwest\",\"typespec_client_core/reqwest_rustls\"],\"test\":[\"typespec_client_core/test\"],\"tokio\":[\"dep:tokio\",\"typespec_client_core/tokio\"],\"xml\":[\"typespec_client_core/xml\"]}}", + "azure_core_macros_1.0.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.106\"},{\"name\":\"quote\",\"req\":\"^1.0.44\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.115\"},{\"default_features\":false,\"features\":[\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.49\"},{\"name\":\"tracing\",\"req\":\"^0.1.44\"},{\"features\":[\"env-filter\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{}}", + "azure_identity_1.0.0": "{\"dependencies\":[{\"name\":\"async-lock\",\"req\":\"^3.4\"},{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"azure_core\",\"req\":\"^1.0.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.5.58\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"include-file\",\"req\":\"^0.5.1\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.79\"},{\"name\":\"pin-project\",\"req\":\"^1.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0.149\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.149\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^3.3\"},{\"features\":[\"serde-well-known\",\"macros\"],\"name\":\"time\",\"req\":\"^0.3.47\"},{\"default_features\":false,\"features\":[\"macros\",\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.49\"},{\"default_features\":false,\"features\":[\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.49\"},{\"name\":\"tracing\",\"req\":\"^0.1.44\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"req\":\"^2.5\"}],\"features\":{\"client_certificate\":[\"openssl\"],\"default\":[\"azure_core/default\"],\"tokio\":[\"dep:tokio\",\"azure_core/tokio\",\"tokio/process\"]}}", + "azure_storage_blob_1.0.0": "{\"dependencies\":[{\"name\":\"async-stream\",\"req\":\"^0.3.6\"},{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"xml\"],\"name\":\"azure_core\",\"req\":\"^1.0.0\"},{\"name\":\"bytes\",\"req\":\"^1.11.1\"},{\"features\":[\"derive\",\"env\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.5.58\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.1.9\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"features\":[\"trace\"],\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.31\"},{\"kind\":\"dev\",\"name\":\"opentelemetry-stdout\",\"req\":\"^0.31\"},{\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"name\":\"pin-project\",\"req\":\"^1.1\"},{\"features\":[\"sys_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0.149\"},{\"features\":[\"serde-well-known\",\"macros\"],\"name\":\"time\",\"req\":\"^0.3.47\"},{\"default_features\":false,\"features\":[\"macros\",\"time\",\"fs\",\"io-util\",\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.49\"},{\"default_features\":false,\"features\":[\"macros\",\"time\",\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.49\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.44\"},{\"features\":[\"env-filter\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"default\":[\"tokio\",\"azure_core/default\"],\"tokio\":[\"dep:tokio\",\"azure_core/tokio\"]}}", "backon_1.6.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"name\":\"embassy-time\",\"optional\":true,\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"fastrand\",\"req\":\"^2\"},{\"name\":\"futures-timer\",\"optional\":true,\"req\":\"^3.0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"gloo-timers\"],\"name\":\"futures-timer\",\"optional\":true,\"req\":\"^3.0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"gloo-timers\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"spin\",\"req\":\"^0.10.0\"},{\"features\":[\"runtime-tokio\",\"sqlite\"],\"kind\":\"dev\",\"name\":\"sqlx\",\"req\":\"^0.8.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\",\"sync\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"time\",\"rt\",\"macros\",\"sync\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"}],\"features\":{\"default\":[\"std\",\"std-blocking-sleep\",\"tokio-sleep\",\"gloo-timers-sleep\"],\"embassy-sleep\":[\"embassy-time\"],\"futures-timer-sleep\":[\"futures-timer\"],\"gloo-timers-sleep\":[\"gloo-timers/futures\"],\"std\":[\"fastrand/std\"],\"std-blocking-sleep\":[],\"tokio-sleep\":[\"tokio/time\"]}}", "base16ct_0.2.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"std\":[\"alloc\"]}}", "base64-simd_0.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.20.0\"},{\"kind\":\"dev\",\"name\":\"const-str\",\"req\":\"^0.5.3\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.8\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"outref\",\"req\":\"^0.5.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"name\":\"vsimd\",\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.33\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"}],\"features\":{\"alloc\":[\"vsimd/alloc\"],\"default\":[\"std\",\"detect\"],\"detect\":[\"vsimd/detect\"],\"std\":[\"alloc\",\"vsimd/std\"],\"unstable\":[\"vsimd/unstable\"]}}", @@ -856,7 +856,6 @@ "cc_1.2.63": "{\"dependencies\":[{\"name\":\"find-msvc-tools\",\"req\":\"^0.1.9\"},{\"default_features\":false,\"name\":\"jobserver\",\"optional\":true,\"req\":\"^0.1.30\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(unix)\"},{\"name\":\"shlex\",\"req\":\"^2.0.1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"jobserver\":[],\"parallel\":[\"dep:libc\",\"dep:jobserver\"]}}", "cesu8_1.1.0": "{\"dependencies\":[],\"features\":{\"unstable\":[]}}", "cfg-if_1.0.4": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"rustc-dep-of-std\":[\"core\"]}}", - "cfg_aliases_0.2.1": "{\"dependencies\":[],\"features\":{}}", "chacha20_0.10.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"features\":[\"stream-wrapper\"],\"name\":\"cipher\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"cipher\",\"req\":\"^0.5\"},{\"name\":\"cpufeatures\",\"req\":\"^0.3\",\"target\":\"cfg(any(target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.8.1\"}],\"features\":{\"default\":[\"cipher\"],\"legacy\":[\"cipher\"],\"rng\":[\"dep:rand_core\"],\"xchacha\":[\"cipher\"]}}", "chrono_0.4.44": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.0\"},{\"name\":\"defmt\",\"optional\":true,\"req\":\"^1.0.1\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1.45\",\"target\":\"cfg(unix)\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pure-rust-locales\",\"optional\":true,\"req\":\"^0.8.2\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.43\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.99\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.66\"},{\"name\":\"windows-link\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_bench\":[],\"alloc\":[],\"clock\":[\"winapi\",\"iana-time-zone\",\"now\"],\"core-error\":[],\"default\":[\"clock\",\"std\",\"oldtime\",\"wasmbind\"],\"defmt\":[\"dep:defmt\",\"pure-rust-locales?/defmt\"],\"libc\":[],\"now\":[\"std\"],\"oldtime\":[],\"rkyv\":[\"dep:rkyv\",\"rkyv/size_32\"],\"rkyv-16\":[\"dep:rkyv\",\"rkyv?/size_16\"],\"rkyv-32\":[\"dep:rkyv\",\"rkyv?/size_32\"],\"rkyv-64\":[\"dep:rkyv\",\"rkyv?/size_64\"],\"rkyv-validation\":[\"rkyv?/validation\"],\"std\":[\"alloc\"],\"unstable-locales\":[\"pure-rust-locales\"],\"wasmbind\":[\"wasm-bindgen\",\"js-sys\"],\"winapi\":[\"windows-link\"]}}", "ciborium-io_0.2.2": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"std\":[\"alloc\"]}}", @@ -931,9 +930,7 @@ "equivalent_1.0.2": "{\"dependencies\":[],\"features\":{}}", "errno_0.3.14": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"hermit\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Diagnostics_Debug\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"libc/std\"]}}", "event-listener-strategy_0.5.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"event-listener/loom\"],\"portable-atomic\":[\"event-listener/portable-atomic\"],\"std\":[\"event-listener/std\"]}}", - "event-listener_2.5.3": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1\"}],\"features\":{}}", "event-listener_5.4.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"concurrent-queue\",\"req\":\"^2.4.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1.2.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"critical-section\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"parking\",\"optional\":true,\"req\":\"^2.0.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"portable_atomic_crate\",\"optional\":true,\"package\":\"portable-atomic\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"try-lock\",\"req\":\"^0.2.5\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"concurrent-queue/loom\",\"parking?/loom\",\"dep:loom\"],\"portable-atomic\":[\"portable-atomic-util\",\"portable_atomic_crate\",\"concurrent-queue/portable-atomic\"],\"std\":[\"concurrent-queue/std\",\"parking\"]}}", - "fastrand_1.9.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"instant\",\"req\":\"^0.1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"features\":[\"wasm-bindgen\"],\"kind\":\"dev\",\"name\":\"instant\",\"req\":\"^0.1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"kind\":\"dev\",\"name\":\"wyhash\",\"req\":\"^0.5\"}],\"features\":{}}", "fastrand_2.3.0": "{\"dependencies\":[{\"features\":[\"js\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wyhash\",\"req\":\"^0.5\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"js\":[\"std\",\"getrandom\"],\"std\":[\"alloc\"]}}", "fastrand_2.4.1": "{\"dependencies\":[{\"features\":[\"wasm_js\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.4\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3.4\"},{\"features\":[\"wasm_js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3.4\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wyhash\",\"req\":\"^0.6\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"js\":[\"std\",\"getrandom\"],\"std\":[\"alloc\"]}}", "ff_0.13.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitvec\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"blake2b_simd\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"byteorder\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"ff_derive\",\"optional\":true,\"req\":\"^0.13.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"subtle\",\"req\":\"^2.2.1\"}],\"features\":{\"alloc\":[],\"bits\":[\"bitvec\"],\"default\":[\"bits\",\"std\"],\"derive\":[\"byteorder\",\"ff_derive\"],\"derive_bits\":[\"bits\",\"ff_derive/bits\"],\"std\":[\"alloc\"]}}", @@ -953,17 +950,15 @@ "futures-core_0.3.32": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1.3\"}],\"features\":{\"alloc\":[],\"cfg-target-has-atomic\":[],\"default\":[\"std\"],\"std\":[\"alloc\"],\"unstable\":[]}}", "futures-executor_0.3.31": "{\"dependencies\":[{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-task\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-util\",\"req\":\"^0.3.31\"},{\"name\":\"num_cpus\",\"optional\":true,\"req\":\"^1.8.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"futures-core/std\",\"futures-task/std\",\"futures-util/std\"],\"thread-pool\":[\"std\",\"num_cpus\"]}}", "futures-io_0.3.32": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[],\"unstable\":[]}}", - "futures-lite_1.13.0": "{\"dependencies\":[{\"name\":\"fastrand\",\"optional\":true,\"req\":\"^1.3.4\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.5\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.5\"},{\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.3.3\"},{\"name\":\"parking\",\"optional\":true,\"req\":\"^2.0.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"spin_on\",\"req\":\"^0.1.0\"},{\"name\":\"waker-fn\",\"optional\":true,\"req\":\"^1.0.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\",\"fastrand\",\"futures-io\",\"parking\",\"memchr\",\"waker-fn\"]}}", "futures-macro_0.3.32": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.52\"}],\"features\":{}}", "futures-sink_0.3.32": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "futures-task_0.3.32": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"cfg-target-has-atomic\":[],\"default\":[\"std\"],\"std\":[\"alloc\"],\"unstable\":[]}}", "futures-util_0.3.32": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3.32\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.32\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.32\"},{\"default_features\":false,\"name\":\"futures-macro\",\"optional\":true,\"req\":\"=0.3.32\"},{\"default_features\":false,\"name\":\"futures-sink\",\"optional\":true,\"req\":\"^0.3.32\"},{\"default_features\":false,\"name\":\"futures-task\",\"req\":\"^0.3.32\"},{\"name\":\"futures_01\",\"optional\":true,\"package\":\"futures\",\"req\":\"^0.1.25\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.26\"},{\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.2\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.6\"},{\"default_features\":false,\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.7\"},{\"name\":\"spin\",\"optional\":true,\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^0.1.11\"},{\"name\":\"tokio-io\",\"optional\":true,\"req\":\"^0.1.9\"}],\"features\":{\"alloc\":[\"futures-core/alloc\",\"futures-task/alloc\",\"slab\"],\"async-await\":[],\"async-await-macro\":[\"async-await\",\"futures-macro\"],\"bilock\":[],\"cfg-target-has-atomic\":[],\"channel\":[\"std\",\"futures-channel\"],\"compat\":[\"std\",\"futures_01\",\"libc\"],\"default\":[\"std\",\"async-await\",\"async-await-macro\"],\"io\":[\"std\",\"futures-io\",\"memchr\"],\"io-compat\":[\"io\",\"compat\",\"tokio-io\",\"libc\"],\"portable-atomic\":[\"futures-core/portable-atomic\"],\"sink\":[\"futures-sink\"],\"std\":[\"alloc\",\"futures-core/std\",\"futures-task/std\",\"slab/std\"],\"unstable\":[\"futures-core/unstable\",\"futures-task/unstable\"],\"write-all-vectored\":[\"io\"]}}", "futures_0.3.31": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.3.0\"},{\"default_features\":false,\"features\":[\"sink\"],\"name\":\"futures-channel\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-executor\",\"optional\":true,\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-io\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-sink\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-task\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"features\":[\"sink\"],\"name\":\"futures-util\",\"req\":\"^0.3.31\"},{\"kind\":\"dev\",\"name\":\"pin-project\",\"req\":\"^1.0.11\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^0.1.11\"}],\"features\":{\"alloc\":[\"futures-core/alloc\",\"futures-task/alloc\",\"futures-sink/alloc\",\"futures-channel/alloc\",\"futures-util/alloc\"],\"async-await\":[\"futures-util/async-await\",\"futures-util/async-await-macro\"],\"bilock\":[\"futures-util/bilock\"],\"cfg-target-has-atomic\":[],\"compat\":[\"std\",\"futures-util/compat\"],\"default\":[\"std\",\"async-await\",\"executor\"],\"executor\":[\"std\",\"futures-executor/std\"],\"io-compat\":[\"compat\",\"futures-util/io-compat\"],\"std\":[\"alloc\",\"futures-core/std\",\"futures-task/std\",\"futures-io/std\",\"futures-sink/std\",\"futures-util/std\",\"futures-util/io\",\"futures-util/channel\"],\"thread-pool\":[\"executor\",\"futures-executor/thread-pool\"],\"unstable\":[\"futures-core/unstable\",\"futures-task/unstable\",\"futures-channel/unstable\",\"futures-io/unstable\",\"futures-util/unstable\"],\"write-all-vectored\":[\"futures-util/write-all-vectored\"]}}", - "gcloud-auth_1.2.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"ctor\",\"req\":\"^0.5\"},{\"name\":\"google-cloud-metadata\",\"package\":\"gcloud-metadata\",\"req\":\"^1.0.1\"},{\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"home\",\"req\":\"^0.5\"},{\"default_features\":false,\"features\":[\"use_pem\"],\"name\":\"jsonwebtoken\",\"req\":\"^10.2\"},{\"name\":\"path-clean\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.3\"},{\"default_features\":false,\"features\":[\"json\",\"charset\"],\"name\":\"reqwest\",\"req\":\"^0.12.4\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"features\":[\"async_closure\"],\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.8.0\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"name\":\"time\",\"req\":\"^0.3\"},{\"name\":\"token-source\",\"req\":\"^1.0\"},{\"features\":[\"fs\"],\"name\":\"tokio\",\"req\":\"^1.32\"},{\"features\":[\"test-util\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.32\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"name\":\"urlencoding\",\"req\":\"^2.1\"}],\"features\":{\"default\":[\"default-tls\",\"jwt-aws-lc-rs\"],\"default-tls\":[\"reqwest/default-tls\"],\"external-account\":[\"sha2\",\"path-clean\",\"url\",\"percent-encoding\",\"hmac\",\"hex\"],\"hickory-dns\":[\"reqwest/hickory-dns\"],\"jwt-aws-lc-rs\":[\"jsonwebtoken/aws_lc_rs\"],\"jwt-rust-crypto\":[\"jsonwebtoken/rust_crypto\"],\"rustls-tls\":[\"reqwest/rustls-tls\"]}}", + "gcloud-auth_1.3.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"ctor\",\"req\":\"^0.5\"},{\"name\":\"google-cloud-metadata\",\"package\":\"gcloud-metadata\",\"req\":\"^1.0.2\"},{\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"home\",\"req\":\"^0.5\"},{\"default_features\":false,\"features\":[\"use_pem\"],\"name\":\"jsonwebtoken\",\"req\":\"^10.2\"},{\"name\":\"path-clean\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.3\"},{\"default_features\":false,\"features\":[\"json\",\"charset\",\"form\"],\"name\":\"reqwest\",\"req\":\"^0.13\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"features\":[\"async_closure\"],\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.8.0\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"name\":\"time\",\"req\":\"^0.3\"},{\"name\":\"token-source\",\"req\":\"^1.0\"},{\"features\":[\"fs\"],\"name\":\"tokio\",\"req\":\"^1.32\"},{\"features\":[\"test-util\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.32\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"name\":\"urlencoding\",\"req\":\"^2.1\"}],\"features\":{\"default\":[\"rustls-tls\",\"jwt-aws-lc-rs\"],\"external-account\":[\"sha2\",\"path-clean\",\"url\",\"percent-encoding\",\"hmac\",\"hex\"],\"hickory-dns\":[\"reqwest/hickory-dns\"],\"jwt-aws-lc-rs\":[\"jsonwebtoken/aws_lc_rs\"],\"jwt-rust-crypto\":[\"jsonwebtoken/rust_crypto\"],\"native-tls\":[\"reqwest/native-tls\"],\"rustls-no-provider\":[\"reqwest/rustls-no-provider\"],\"rustls-tls\":[\"reqwest/rustls\"]}}", "gcloud-metadata_1.0.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"reqwest\",\"req\":\"^0.13\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"features\":[\"sync\",\"net\",\"parking_lot\"],\"name\":\"tokio\",\"req\":\"^1.32\"},{\"features\":[\"test-util\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.32\"}],\"features\":{}}", - "gcloud-storage_1.1.1": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.5\"},{\"kind\":\"dev\",\"name\":\"ctor\",\"req\":\"^0.5\"},{\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"google-cloud-auth\",\"optional\":true,\"package\":\"gcloud-auth\",\"req\":\"^1.1.2\"},{\"name\":\"google-cloud-metadata\",\"optional\":true,\"package\":\"gcloud-metadata\",\"req\":\"^1.0.1\"},{\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1.18\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"features\":[\"pem\"],\"name\":\"pkcs8\",\"req\":\"^0.10\"},{\"name\":\"regex\",\"req\":\"^1.9\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"features\":[\"json\",\"multipart\"],\"name\":\"reqwest-middleware\",\"req\":\"^0.4\"},{\"name\":\"ring\",\"req\":\"^0.17\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^3.1\"},{\"name\":\"sha2\",\"req\":\"^0.10\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"features\":[\"std\",\"macros\",\"formatting\",\"parsing\",\"serde\"],\"name\":\"time\",\"req\":\"^0.3\"},{\"name\":\"token-source\",\"req\":\"^1.0\"},{\"features\":[\"macros\"],\"name\":\"tokio\",\"req\":\"^1.32\"},{\"features\":[\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.32\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.17\"},{\"name\":\"url\",\"req\":\"^2.4\"}],\"features\":{\"auth\":[\"google-cloud-auth\",\"google-cloud-metadata\"],\"default\":[\"default-tls\",\"auth\"],\"default-tls\":[\"reqwest/default-tls\",\"google-cloud-auth?/default-tls\"],\"external-account\":[\"google-cloud-auth?/external-account\"],\"hickory-dns\":[\"reqwest/hickory-dns\",\"google-cloud-auth?/hickory-dns\"],\"rustls-tls\":[\"reqwest/rustls-tls\",\"google-cloud-auth?/rustls-tls\"],\"trace\":[]}}", + "gcloud-storage_1.3.0": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.5\"},{\"kind\":\"dev\",\"name\":\"ctor\",\"req\":\"^0.5\"},{\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"google-cloud-auth\",\"optional\":true,\"package\":\"gcloud-auth\",\"req\":\"^1.3.0\"},{\"name\":\"google-cloud-metadata\",\"optional\":true,\"package\":\"gcloud-metadata\",\"req\":\"^1.0.2\"},{\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1.18\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"features\":[\"pem\"],\"name\":\"pkcs8\",\"req\":\"^0.10\"},{\"name\":\"regex\",\"req\":\"^1.9\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"name\":\"reqwest\",\"req\":\"^0.13\"},{\"features\":[\"json\",\"multipart\",\"query\"],\"name\":\"reqwest-middleware\",\"req\":\"^0.5\"},{\"name\":\"ring\",\"req\":\"^0.17\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^3.1\"},{\"name\":\"sha2\",\"req\":\"^0.10\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"features\":[\"std\",\"macros\",\"formatting\",\"parsing\",\"serde\"],\"name\":\"time\",\"req\":\"^0.3\"},{\"name\":\"token-source\",\"req\":\"^1.0\"},{\"features\":[\"macros\"],\"name\":\"tokio\",\"req\":\"^1.32\"},{\"features\":[\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.32\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.17\"},{\"name\":\"url\",\"req\":\"^2.4\"}],\"features\":{\"auth\":[\"google-cloud-auth\",\"google-cloud-metadata\"],\"default\":[\"rustls-tls\",\"auth\",\"jwt-aws-lc-rs\"],\"external-account\":[\"google-cloud-auth?/external-account\"],\"hickory-dns\":[\"reqwest/hickory-dns\",\"google-cloud-auth?/hickory-dns\"],\"jwt-aws-lc-rs\":[\"google-cloud-auth?/jwt-aws-lc-rs\"],\"jwt-rust-crypto\":[\"google-cloud-auth?/jwt-rust-crypto\"],\"native-tls\":[\"google-cloud-auth?/native-tls\"],\"rustls-no-provider\":[\"google-cloud-auth?/rustls-no-provider\"],\"rustls-tls\":[\"google-cloud-auth?/rustls-tls\"],\"trace\":[]}}", "generic-array_0.14.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"typenum\",\"req\":\"^1.12\"},{\"kind\":\"build\",\"name\":\"version_check\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"more_lengths\":[]}}", - "getrandom_0.1.16": "{\"dependencies\":[{\"name\":\"bindgen\",\"optional\":true,\"package\":\"wasm-bindgen\",\"req\":\"^0.2.29\",\"target\":\"wasm32-unknown-unknown\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"wasm32-unknown-unknown\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.64\",\"target\":\"cfg(unix)\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"stdweb\",\"optional\":true,\"req\":\"^0.4.18\",\"target\":\"wasm32-unknown-unknown\"},{\"name\":\"wasi\",\"req\":\"^0.9\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.2\",\"target\":\"wasm32-unknown-unknown\"}],\"features\":{\"dummy\":[],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\"],\"std\":[],\"test-in-browser\":[\"wasm-bindgen\"],\"wasm-bindgen\":[\"bindgen\",\"js-sys\"]}}", "getrandom_0.2.16": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"wasi\",\"req\":\"^0.11\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.18\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"}],\"features\":{\"custom\":[],\"js\":[\"wasm-bindgen\",\"js-sys\"],\"linux_disable_fallback\":[],\"rdrand\":[],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\",\"libc/rustc-dep-of-std\",\"wasi/rustc-dep-of-std\"],\"std\":[],\"test-in-browser\":[]}}", "getrandom_0.3.4": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), not(any(all(target_os = \\\"linux\\\", target_env = \\\"\\\"), getrandom_backend = \\\"custom\\\", getrandom_backend = \\\"linux_raw\\\", getrandom_backend = \\\"rdrand\\\", getrandom_backend = \\\"rndr\\\"))))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"hurd\\\", target_os = \\\"illumos\\\", target_os = \\\"cygwin\\\", all(target_os = \\\"horizon\\\", target_arch = \\\"arm\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"haiku\\\", target_os = \\\"redox\\\", target_os = \\\"nto\\\", target_os = \\\"aix\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\", target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"openbsd\\\", target_os = \\\"vita\\\", target_os = \\\"emscripten\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"netbsd\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"solaris\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"vxworks\\\")\"},{\"default_features\":false,\"name\":\"r-efi\",\"req\":\"^5.1\",\"target\":\"cfg(all(target_os = \\\"uefi\\\", getrandom_backend = \\\"efi_rng\\\"))\"},{\"default_features\":false,\"name\":\"wasip2\",\"req\":\"^1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p2\\\"))\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"}],\"features\":{\"std\":[],\"wasm_js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"]}}", "getrandom_0.4.2": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), not(any(all(target_os = \\\"linux\\\", target_env = \\\"\\\"), getrandom_backend = \\\"custom\\\", getrandom_backend = \\\"linux_raw\\\", getrandom_backend = \\\"rdrand\\\", getrandom_backend = \\\"rndr\\\"))))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"hurd\\\", target_os = \\\"illumos\\\", target_os = \\\"cygwin\\\", all(target_os = \\\"horizon\\\", target_arch = \\\"arm\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"haiku\\\", target_os = \\\"redox\\\", target_os = \\\"nto\\\", target_os = \\\"aix\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\", target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"openbsd\\\", target_os = \\\"vita\\\", target_os = \\\"emscripten\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"netbsd\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"solaris\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"vxworks\\\")\"},{\"default_features\":false,\"name\":\"r-efi\",\"req\":\"^6\",\"target\":\"cfg(all(target_os = \\\"uefi\\\", getrandom_backend = \\\"efi_rng\\\"))\"},{\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.10.0\"},{\"default_features\":false,\"name\":\"wasip2\",\"req\":\"^1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p2\\\"))\"},{\"name\":\"wasip3\",\"req\":\"^0.4\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p3\\\"))\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"}],\"features\":{\"std\":[],\"sys_rng\":[\"dep:rand_core\"],\"wasm_js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"]}}", @@ -988,7 +983,6 @@ "http-body-util_0.1.3": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"macros\",\"rt\",\"sync\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{\"channel\":[\"dep:tokio\"],\"default\":[],\"full\":[\"channel\"]}}", "http-body_0.4.6": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"http\",\"req\":\"^0.2\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{}}", "http-body_1.0.1": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"http\",\"req\":\"^1\"}],\"features\":{}}", - "http-types_2.12.0": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.26\"},{\"name\":\"async-channel\",\"req\":\"^1.5.1\"},{\"name\":\"async-std\",\"optional\":true,\"req\":\"^1.6.0\"},{\"features\":[\"attributes\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.6.0\"},{\"name\":\"base64\",\"req\":\"^0.13.0\"},{\"features\":[\"percent-encode\"],\"name\":\"cookie\",\"optional\":true,\"req\":\"^0.14.0\"},{\"name\":\"futures-lite\",\"req\":\"^1.11.1\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^0.2.0\"},{\"name\":\"infer\",\"req\":\"^0.2.3\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.0\"},{\"name\":\"rand\",\"req\":\"^0.7.3\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.106\"},{\"name\":\"serde_json\",\"req\":\"^1.0.51\"},{\"name\":\"serde_qs\",\"req\":\"^0.8.3\"},{\"name\":\"serde_urlencoded\",\"req\":\"^0.7.0\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.1.1\"}],\"features\":{\"async_std\":[\"fs\"],\"cookie-secure\":[\"cookies\",\"cookie/secure\"],\"cookies\":[\"cookie\"],\"default\":[\"fs\",\"cookie-secure\"],\"docs\":[\"unstable\"],\"fs\":[\"async-std\"],\"hyperium_http\":[\"http\"],\"unstable\":[]}}", "http_0.2.12": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"indexmap\",\"req\":\"<=1.8\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7.0\"},{\"kind\":\"dev\",\"name\":\"seahash\",\"req\":\"^3.0.5\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{}}", "http_1.4.0": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "http_1.4.1": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", @@ -1017,8 +1011,6 @@ "indexmap_1.9.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"raw\"],\"name\":\"hashbrown\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.3\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.4.1\"},{\"name\":\"rustc-rayon\",\"optional\":true,\"package\":\"rustc-rayon\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"}],\"features\":{\"serde-1\":[\"serde\"],\"std\":[],\"test_debug\":[],\"test_low_transition_point\":[]}}", "indexmap_2.13.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"equivalent\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"req\":\"^0.16.1\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.9\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"default\":[\"std\"],\"serde\":[\"dep:serde_core\",\"dep:serde\"],\"std\":[],\"test_debug\":[]}}", "indexmap_2.14.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"equivalent\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"req\":\"^0.17\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.1\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.9\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"default\":[\"std\"],\"serde\":[\"dep:serde_core\",\"dep:serde\"],\"std\":[],\"test_debug\":[]}}", - "infer_0.2.3": "{\"dependencies\":[],\"features\":{}}", - "instant_0.1.13": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"asmjs-unknown-emscripten\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"wasm32-unknown-emscripten\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"wasm32-unknown-unknown\"},{\"name\":\"stdweb\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"asmjs-unknown-emscripten\"},{\"name\":\"stdweb\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"wasm32-unknown-emscripten\"},{\"name\":\"stdweb\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"wasm32-unknown-unknown\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\"},{\"name\":\"wasm-bindgen_rs\",\"optional\":true,\"package\":\"wasm-bindgen\",\"req\":\"^0.2\",\"target\":\"asmjs-unknown-emscripten\"},{\"name\":\"wasm-bindgen_rs\",\"optional\":true,\"package\":\"wasm-bindgen\",\"req\":\"^0.2\",\"target\":\"wasm32-unknown-emscripten\"},{\"name\":\"wasm-bindgen_rs\",\"optional\":true,\"package\":\"wasm-bindgen\",\"req\":\"^0.2\",\"target\":\"wasm32-unknown-unknown\"},{\"features\":[\"Window\",\"Performance\",\"PerformanceTiming\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"asmjs-unknown-emscripten\"},{\"features\":[\"Window\",\"Performance\",\"PerformanceTiming\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"wasm32-unknown-emscripten\"},{\"features\":[\"Window\",\"Performance\",\"PerformanceTiming\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"wasm32-unknown-unknown\"}],\"features\":{\"inaccurate\":[],\"now\":[],\"wasm-bindgen\":[\"js-sys\",\"wasm-bindgen_rs\",\"web-sys\"]}}", "io-lifetimes_2.0.4": "{\"dependencies\":[{\"features\":[\"io_safety\"],\"name\":\"async-std\",\"optional\":true,\"req\":\"^1.13.0\",\"target\":\"cfg(not(target_os = \\\"wasi\\\"))\"},{\"name\":\"hermit-abi\",\"optional\":true,\"req\":\">=0.3, <=0.4\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.96\",\"target\":\"cfg(not(windows))\"},{\"features\":[\"net\",\"os-ext\"],\"name\":\"mio\",\"optional\":true,\"req\":\"^0.8.0\",\"target\":\"cfg(not(target_os = \\\"wasi\\\"))\"},{\"features\":[\"io_safety\"],\"name\":\"os_pipe\",\"optional\":true,\"req\":\"^1.0.0\",\"target\":\"cfg(not(target_os = \\\"wasi\\\"))\"},{\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_os = \\\"wasi\\\"))\"},{\"features\":[\"io-std\",\"fs\",\"net\",\"process\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.6.0\",\"target\":\"cfg(not(target_os = \\\"wasi\\\"))\"},{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_Networking_WinSock\",\"Win32_Security\",\"Win32_System_IO\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\">=0.52, <=0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"close\":[\"libc\",\"hermit-abi\",\"windows-sys\"],\"default\":[]}}", "ipconfig_0.3.4": "{\"dependencies\":[{\"name\":\"socket2\",\"req\":\"^0.6.0\",\"target\":\"cfg(windows)\"},{\"name\":\"widestring\",\"req\":\"^1.0.2\",\"target\":\"cfg(windows)\"},{\"name\":\"windows-registry\",\"optional\":true,\"req\":\"^0.6.1\",\"target\":\"cfg(windows)\"},{\"name\":\"windows-result\",\"optional\":true,\"req\":\"^0.4.1\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_System_Registry\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"computer\":[\"dep:windows-registry\",\"dep:windows-result\"],\"default\":[\"computer\"]}}", "ipnet_2.11.0": "{\"dependencies\":[{\"name\":\"heapless\",\"optional\":true,\"req\":\"^0\"},{\"name\":\"schemars\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"package\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1\"}],\"features\":{\"default\":[\"std\"],\"json\":[\"serde\",\"schemars\"],\"ser_as_str\":[\"heapless\"],\"std\":[]}}", @@ -1036,7 +1028,7 @@ "jni_0.21.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.0\",\"target\":\"cfg(windows)\"},{\"name\":\"cesu8\",\"req\":\"^1.1.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"combine\",\"req\":\"^4.1.0\"},{\"name\":\"java-locator\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"jni-sys\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"name\":\"thiserror\",\"req\":\"^1.0.20\"},{\"kind\":\"build\",\"name\":\"walkdir\",\"req\":\"^2\"},{\"features\":[\"Win32_Globalization\"],\"name\":\"windows-sys\",\"req\":\"^0.45.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"invocation\":[\"java-locator\",\"libloading\"]}}", "jni_0.22.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.0\",\"target\":\"cfg(windows)\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"combine\",\"req\":\"^4.1.0\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"java-locator\",\"optional\":true,\"req\":\"^0.1.3\",\"target\":\"cfg(not(target_os = \\\"android\\\"))\"},{\"kind\":\"dev\",\"name\":\"javac\",\"req\":\"^0.1.0\"},{\"name\":\"jni-macros\",\"req\":\"=0.22.4\"},{\"name\":\"jni-sys\",\"req\":\"^0.4.1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.8\",\"target\":\"cfg(not(target_os = \\\"android\\\"))\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"name\":\"simd_cesu8\",\"req\":\"^1.1.1\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"walkdir\",\"req\":\"^2\"},{\"name\":\"windows-link\",\"req\":\"^0.2\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_System_Threading\",\"Win32_Foundation\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"_cfg_test\":[],\"default\":[],\"invocation\":[\"dep:java-locator\",\"dep:libloading\"]}}", "jobserver_0.1.34": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.3.2\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(unix)\"},{\"features\":[\"fs\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.28.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"}],\"features\":{}}", - "js-sys_0.3.81": "{\"dependencies\":[{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.104\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"wasm-bindgen/std\"]}}", + "js-sys_0.3.102": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.8\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.31\"},{\"kind\":\"dev\",\"name\":\"half\",\"req\":\"^2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.125\"}],\"features\":{\"default\":[\"std\",\"unsafe-eval\"],\"futures-core-03-stream\":[\"dep:futures-util\",\"dep:futures-core\"],\"std\":[\"wasm-bindgen/std\",\"dep:futures-util\"],\"unsafe-eval\":[]}}", "jsonwebtoken_10.3.0": "{\"dependencies\":[{\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.15.0\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\")))))\"},{\"features\":[\"pkcs8\"],\"name\":\"ed25519-dalek\",\"optional\":true,\"req\":\"^2.1.1\"},{\"features\":[\"pkcs8\",\"rand_core\"],\"kind\":\"dev\",\"name\":\"ed25519-dalek\",\"req\":\"^2.1.1\"},{\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12.1\"},{\"name\":\"js-sys\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"ecdsa\"],\"name\":\"p256\",\"optional\":true,\"req\":\"^0.13.2\"},{\"features\":[\"ecdsa\"],\"name\":\"p384\",\"optional\":true,\"req\":\"^0.13.0\"},{\"name\":\"pem\",\"optional\":true,\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8.5\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6.4\"},{\"name\":\"rsa\",\"optional\":true,\"req\":\"^0.9.6\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.228\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"oid\"],\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.7\"},{\"features\":[\"std\"],\"name\":\"signature\",\"req\":\"^2.2.0\"},{\"name\":\"simple_asn1\",\"optional\":true,\"req\":\"^0.6\"},{\"features\":[\"wasm-bindgen\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\")))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.1\"}],\"features\":{\"aws_lc_rs\":[\"aws-lc-rs\"],\"default\":[\"use_pem\"],\"rust_crypto\":[\"ed25519-dalek\",\"hmac\",\"p256\",\"p384\",\"rand\",\"rsa\",\"sha2\"],\"use_pem\":[\"pem\",\"simple_asn1\"]}}", "lazy_static_1.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"once\"],\"name\":\"spin\",\"optional\":true,\"req\":\"^0.9.8\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"}],\"features\":{\"spin_no_std\":[\"spin\"]}}", "leb128fmt_0.1.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[]}}", @@ -1051,7 +1043,6 @@ "lock_api_0.4.14": "{\"dependencies\":[{\"name\":\"owning_ref\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"scopeguard\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.126\"}],\"features\":{\"arc_lock\":[],\"atomic_usize\":[],\"default\":[\"atomic_usize\"],\"nightly\":[]}}", "log_0.4.29": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.63\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval\",\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval_derive\",\"req\":\"^2.16\"},{\"default_features\":false,\"name\":\"sval_ref\",\"optional\":true,\"req\":\"^2.16\"},{\"default_features\":false,\"features\":[\"inline-i128\"],\"name\":\"value-bag\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"test\"],\"kind\":\"dev\",\"name\":\"value-bag\",\"req\":\"^1.12\"}],\"features\":{\"kv\":[],\"kv_serde\":[\"kv_std\",\"value-bag/serde\",\"serde\"],\"kv_std\":[\"std\",\"kv\",\"value-bag/error\"],\"kv_sval\":[\"kv\",\"value-bag/sval\",\"sval\",\"sval_ref\"],\"kv_unstable\":[\"kv\",\"value-bag\"],\"kv_unstable_serde\":[\"kv_serde\",\"kv_unstable_std\"],\"kv_unstable_std\":[\"kv_std\",\"kv_unstable\"],\"kv_unstable_sval\":[\"kv_sval\",\"kv_unstable\"],\"max_level_debug\":[],\"max_level_error\":[],\"max_level_info\":[],\"max_level_off\":[],\"max_level_trace\":[],\"max_level_warn\":[],\"release_max_level_debug\":[],\"release_max_level_error\":[],\"release_max_level_info\":[],\"release_max_level_off\":[],\"release_max_level_trace\":[],\"release_max_level_warn\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", "log_0.4.30": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.63\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval\",\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval_derive\",\"req\":\"^2.16\"},{\"default_features\":false,\"name\":\"sval_ref\",\"optional\":true,\"req\":\"^2.16\"},{\"default_features\":false,\"features\":[\"inline-i128\"],\"name\":\"value-bag\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"test\"],\"kind\":\"dev\",\"name\":\"value-bag\",\"req\":\"^1.12\"}],\"features\":{\"kv\":[],\"kv_serde\":[\"kv_std\",\"value-bag/serde\",\"serde\"],\"kv_std\":[\"std\",\"kv\",\"value-bag/error\"],\"kv_sval\":[\"kv\",\"value-bag/sval\",\"sval\",\"sval_ref\"],\"kv_unstable\":[\"kv\",\"value-bag\"],\"kv_unstable_serde\":[\"kv_serde\",\"kv_unstable_std\"],\"kv_unstable_std\":[\"kv_std\",\"kv_unstable\"],\"kv_unstable_sval\":[\"kv_sval\",\"kv_unstable\"],\"max_level_debug\":[],\"max_level_error\":[],\"max_level_info\":[],\"max_level_off\":[],\"max_level_trace\":[],\"max_level_warn\":[],\"release_max_level_debug\":[],\"release_max_level_error\":[],\"release_max_level_info\":[],\"release_max_level_off\":[],\"release_max_level_trace\":[],\"release_max_level_warn\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", - "lru-slab_0.1.2": "{\"dependencies\":[],\"features\":{}}", "lru_0.16.3": "{\"dependencies\":[{\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.16.0\"},{\"kind\":\"dev\",\"name\":\"scoped_threadpool\",\"req\":\"0.1.*\"},{\"kind\":\"dev\",\"name\":\"stats_alloc\",\"req\":\"0.1.*\"}],\"features\":{\"default\":[\"hashbrown\"],\"nightly\":[\"hashbrown\",\"hashbrown/nightly\"]}}", "lz4_flex_0.11.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"binggan\",\"req\":\"^0.12.0\"},{\"kind\":\"dev\",\"name\":\"jemallocator\",\"req\":\"^0.5.4\"},{\"kind\":\"dev\",\"name\":\"lz4-compress\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"lzzzz\",\"req\":\"^1.0.4\"},{\"kind\":\"dev\",\"name\":\"more-asserts\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.91\"},{\"kind\":\"dev\",\"name\":\"snap\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"xxhash32\"],\"name\":\"twox-hash\",\"optional\":true,\"req\":\"^2.0.0\"}],\"features\":{\"checked-decode\":[],\"default\":[\"std\",\"safe-encode\",\"safe-decode\",\"frame\",\"checked-decode\"],\"frame\":[\"std\",\"dep:twox-hash\"],\"nightly\":[],\"safe-decode\":[],\"safe-encode\":[],\"std\":[]}}", "macro_magic_0.5.1": "{\"dependencies\":[{\"name\":\"macro_magic_core\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"macro_magic_macros\",\"req\":\"^0.5.1\"},{\"name\":\"quote\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"default\":[],\"proc_support\":[\"dep:macro_magic_core\",\"dep:syn\",\"dep:quote\"]}}", @@ -1106,7 +1097,6 @@ "parking_2.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"}],\"features\":{}}", "parking_lot_0.12.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"name\":\"lock_api\",\"req\":\"^0.4.14\"},{\"name\":\"parking_lot_core\",\"req\":\"^0.9.12\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"}],\"features\":{\"arc_lock\":[\"lock_api/arc_lock\"],\"deadlock_detection\":[\"parking_lot_core/deadlock_detection\"],\"default\":[],\"hardware-lock-elision\":[],\"nightly\":[\"parking_lot_core/nightly\",\"lock_api/nightly\"],\"owning_ref\":[\"lock_api/owning_ref\"],\"send_guard\":[],\"serde\":[\"lock_api/serde\"]}}", "parking_lot_core_0.9.12": "{\"dependencies\":[{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.60\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"libc\",\"req\":\"^0.2.95\",\"target\":\"cfg(unix)\"},{\"name\":\"petgraph\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"redox_syscall\",\"req\":\"^0.5\",\"target\":\"cfg(target_os = \\\"redox\\\")\"},{\"name\":\"smallvec\",\"req\":\"^1.6.1\"},{\"name\":\"windows-link\",\"req\":\"^0.2.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"deadlock_detection\":[\"petgraph\",\"backtrace\"],\"nightly\":[]}}", - "paste_1.0.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"paste-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", "pastey_0.2.2": "{\"dependencies\":[],\"features\":{}}", "pathdiff_0.2.3": "{\"dependencies\":[{\"name\":\"camino\",\"optional\":true,\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1.0.0\"}],\"features\":{}}", "patricia_tree_0.9.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"}],\"features\":{}}", @@ -1155,26 +1145,19 @@ "pyo3-macros-backend_0.28.3": "{\"dependencies\":[{\"name\":\"heck\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"features\":[\"resolve-config\"],\"name\":\"pyo3-build-config\",\"req\":\"=0.28.3\"},{\"kind\":\"build\",\"name\":\"pyo3-build-config\",\"req\":\"=0.28.3\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0.37\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"full\",\"extra-traits\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0.59\"}],\"features\":{\"experimental-async\":[],\"experimental-inspect\":[]}}", "pyo3-macros_0.28.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"pyo3-macros-backend\",\"req\":\"=0.28.3\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"experimental-async\":[\"pyo3-macros-backend/experimental-async\"],\"experimental-inspect\":[\"pyo3-macros-backend/experimental-inspect\"],\"multiple-pymethods\":[]}}", "pyo3_0.28.3": "{\"dependencies\":[{\"name\":\"anyhow\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"assert_approx_eq\",\"req\":\"^1.1.0\"},{\"name\":\"bigdecimal\",\"optional\":true,\"req\":\"^0.4.7\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.25\"},{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.25\"},{\"default_features\":false,\"name\":\"chrono-tz\",\"optional\":true,\"req\":\">=0.10, <0.11\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\">=0.10, <0.11\"},{\"name\":\"either\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"eyre\",\"optional\":true,\"req\":\">=0.6.8, <0.7\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\">=0.15.0, <0.17\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\">=2.5.0, <3\"},{\"name\":\"inventory\",\"optional\":true,\"req\":\"^0.3.5\"},{\"name\":\"jiff-02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"name\":\"libc\",\"req\":\"^0.2.62\"},{\"name\":\"lock_api\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.4\"},{\"name\":\"num-complex\",\"optional\":true,\"req\":\">=0.4.6, <0.5\"},{\"name\":\"num-rational\",\"optional\":true,\"req\":\"^0.4.1\"},{\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.16\"},{\"name\":\"once_cell\",\"req\":\"^1.21\"},{\"default_features\":false,\"name\":\"ordered-float\",\"optional\":true,\"req\":\"^5.0.0\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12\"},{\"features\":[\"arc_lock\"],\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.3\"},{\"name\":\"portable-atomic\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_has_atomic = \\\"64\\\"))\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.0\"},{\"features\":[\"resolve-config\"],\"kind\":\"build\",\"name\":\"pyo3-build-config\",\"req\":\"=0.28.3\"},{\"name\":\"pyo3-ffi\",\"req\":\"=0.28.3\"},{\"name\":\"pyo3-macros\",\"optional\":true,\"req\":\"=0.28.3\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.6.1\"},{\"default_features\":false,\"name\":\"rust_decimal\",\"optional\":true,\"req\":\"^1.15\"},{\"kind\":\"dev\",\"name\":\"send_wrapper\",\"req\":\"^0.6\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.61\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.12.0\"},{\"default_features\":false,\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.38\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\">=1.0.115\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.12.0\"},{\"features\":[\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.10.0\"}],\"features\":{\"abi3\":[\"pyo3-build-config/abi3\",\"pyo3-ffi/abi3\"],\"abi3-py310\":[\"abi3-py311\",\"pyo3-build-config/abi3-py310\",\"pyo3-ffi/abi3-py310\"],\"abi3-py311\":[\"abi3-py312\",\"pyo3-build-config/abi3-py311\",\"pyo3-ffi/abi3-py311\"],\"abi3-py312\":[\"abi3-py313\",\"pyo3-build-config/abi3-py312\",\"pyo3-ffi/abi3-py312\"],\"abi3-py313\":[\"abi3-py314\",\"pyo3-build-config/abi3-py313\",\"pyo3-ffi/abi3-py313\"],\"abi3-py314\":[\"abi3\",\"pyo3-build-config/abi3-py314\",\"pyo3-ffi/abi3-py314\"],\"abi3-py37\":[\"abi3-py38\",\"pyo3-build-config/abi3-py37\",\"pyo3-ffi/abi3-py37\"],\"abi3-py38\":[\"abi3-py39\",\"pyo3-build-config/abi3-py38\",\"pyo3-ffi/abi3-py38\"],\"abi3-py39\":[\"abi3-py310\",\"pyo3-build-config/abi3-py39\",\"pyo3-ffi/abi3-py39\"],\"arc_lock\":[\"lock_api\",\"lock_api/arc_lock\",\"parking_lot?/arc_lock\"],\"auto-initialize\":[],\"bigdecimal\":[\"dep:bigdecimal\",\"num-bigint\"],\"chrono-local\":[\"chrono/clock\",\"dep:iana-time-zone\"],\"default\":[\"macros\"],\"experimental-async\":[\"macros\",\"pyo3-macros/experimental-async\"],\"experimental-inspect\":[\"pyo3-macros/experimental-inspect\"],\"extension-module\":[\"pyo3-ffi/extension-module\"],\"full\":[\"macros\",\"anyhow\",\"arc_lock\",\"bigdecimal\",\"bytes\",\"chrono\",\"chrono-local\",\"chrono-tz\",\"either\",\"experimental-async\",\"experimental-inspect\",\"eyre\",\"hashbrown\",\"indexmap\",\"jiff-02\",\"lock_api\",\"num-bigint\",\"num-complex\",\"num-rational\",\"ordered-float\",\"parking_lot\",\"py-clone\",\"rust_decimal\",\"serde\",\"smallvec\",\"time\",\"uuid\"],\"generate-import-lib\":[\"pyo3-ffi/generate-import-lib\"],\"macros\":[\"pyo3-macros\"],\"multiple-pymethods\":[\"inventory\",\"pyo3-macros/multiple-pymethods\"],\"nightly\":[],\"num-bigint\":[\"dep:num-bigint\",\"dep:num-traits\"],\"parking_lot\":[\"dep:parking_lot\",\"lock_api\"],\"py-clone\":[]}}", - "quick-xml_0.31.0": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"memchr\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\">=1.0.100\"},{\"kind\":\"dev\",\"name\":\"serde-value\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.79\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"}],\"features\":{\"async-tokio\":[\"tokio\"],\"default\":[],\"encoding\":[\"encoding_rs\"],\"escape-html\":[],\"overlapped-lists\":[],\"serde-types\":[\"serde/derive\"],\"serialize\":[\"serde\"]}}", - "quinn-proto_0.11.15": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"fastbloom\",\"optional\":true,\"req\":\"^0.14\"},{\"default_features\":false,\"features\":[\"wasm_js\"],\"name\":\"getrandom\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"lru-slab\",\"req\":\"^0.1.2\"},{\"name\":\"qlog\",\"optional\":true,\"req\":\"^0.15.2\"},{\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"features\":[\"wasm32_unknown_unknown_js\"],\"name\":\"ring\",\"req\":\"^0.17\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"features\":[\"web\"],\"name\":\"rustls-pki-types\",\"req\":\"^1.7\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"slab\",\"req\":\"^0.4.6\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"alloc\",\"alloc\"],\"name\":\"tinyvec\",\"req\":\"^1.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.45\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"__rustls-post-quantum-test\":[],\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"aws-lc-rs?/aws-lc-sys\",\"aws-lc-rs?/prebuilt-nasm\"],\"aws-lc-rs-fips\":[\"aws-lc-rs\",\"aws-lc-rs?/fips\"],\"bloom\":[\"dep:fastbloom\"],\"default\":[\"rustls-ring\",\"log\",\"bloom\"],\"log\":[\"tracing/log\"],\"platform-verifier\":[\"dep:rustls-platform-verifier\"],\"qlog\":[\"dep:qlog\"],\"ring\":[\"dep:ring\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"rustls?/aws-lc-rs\",\"aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"rustls-aws-lc-rs\",\"aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"rustls?/ring\",\"ring\"]}}", - "quinn-udp_0.5.14": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"libc\",\"req\":\"^0.2.158\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1.19\",\"target\":\"cfg(windows)\"},{\"name\":\"socket2\",\"req\":\">=0.5, <0.7\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"features\":[\"sync\",\"rt\",\"rt-multi-thread\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.10\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_IO\",\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <=0.60\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"tracing\",\"log\"],\"direct-log\":[\"dep:log\"],\"fast-apple-datapath\":[],\"log\":[\"tracing/log\"]}}", - "quinn_0.11.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.22\"},{\"name\":\"async-io\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"async-std\",\"optional\":true,\"req\":\"^1.11\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"crc\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"directories-next\",\"req\":\"^2\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.19\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"proto\",\"package\":\"quinn-proto\",\"req\":\"^0.11.12\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"kind\":\"dev\",\"name\":\"rustls-pemfile\",\"req\":\"^2\"},{\"name\":\"smol\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"socket2\",\"req\":\">=0.5, <0.7\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"features\":[\"sync\",\"rt\",\"rt-multi-thread\",\"time\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"std-future\"],\"kind\":\"dev\",\"name\":\"tracing-futures\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"features\":[\"tracing\"],\"name\":\"udp\",\"package\":\"quinn-udp\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"aws-lc-rs\":[\"proto/aws-lc-rs\"],\"aws-lc-rs-fips\":[\"proto/aws-lc-rs-fips\"],\"bloom\":[\"proto/bloom\"],\"default\":[\"log\",\"platform-verifier\",\"runtime-tokio\",\"rustls-ring\",\"bloom\"],\"lock_tracking\":[],\"log\":[\"tracing/log\",\"proto/log\",\"udp/log\"],\"platform-verifier\":[\"proto/platform-verifier\"],\"qlog\":[\"proto/qlog\"],\"ring\":[\"proto/ring\"],\"runtime-async-std\":[\"async-io\",\"async-std\"],\"runtime-smol\":[\"async-io\",\"smol\"],\"runtime-tokio\":[\"tokio/time\",\"tokio/rt\",\"tokio/net\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"aws-lc-rs\",\"proto/rustls-aws-lc-rs\",\"proto/aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"dep:rustls\",\"aws-lc-rs-fips\",\"proto/rustls-aws-lc-rs-fips\",\"proto/aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"ring\",\"proto/rustls-ring\",\"proto/ring\"]}}", + "quick-xml_0.39.4": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\">=0.4, <0.9\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"memchr\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\">=1.0.180\"},{\"kind\":\"dev\",\"name\":\"serde-value\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.206\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"}],\"features\":{\"async-tokio\":[\"tokio\"],\"default\":[],\"encoding\":[\"encoding_rs\"],\"escape-html\":[],\"overlapped-lists\":[],\"serde-types\":[\"serde/derive\"],\"serialize\":[\"serde\"]}}", "quote_1.0.45": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"proc-macro\"],\"proc-macro\":[\"proc-macro2/proc-macro\"]}}", "r-efi_5.3.0": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"efiapi\":[],\"examples\":[\"native\"],\"native\":[],\"rustc-dep-of-std\":[\"core\"]}}", "r-efi_6.0.0": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"native\":[],\"rustc-dep-of-std\":[\"core\"]}}", "radium_0.7.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"}],\"features\":{}}", "rand_0.10.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"rng\"],\"name\":\"chacha20\",\"optional\":true,\"req\":\"^0.10.0\"},{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.1.3\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.7\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"}],\"features\":{\"alloc\":[],\"chacha\":[\"dep:chacha20\"],\"default\":[\"std\",\"std_rng\",\"sys_rng\",\"thread_rng\"],\"log\":[],\"serde\":[\"dep:serde\"],\"simd_support\":[],\"std\":[\"alloc\",\"getrandom?/std\"],\"std_rng\":[\"dep:chacha20\"],\"sys_rng\":[\"dep:getrandom\",\"getrandom/sys_rng\"],\"thread_rng\":[\"std\",\"std_rng\",\"sys_rng\"],\"unbiased\":[]}}", - "rand_0.7.3": "{\"dependencies\":[{\"name\":\"getrandom_package\",\"optional\":true,\"package\":\"getrandom\",\"req\":\"^0.1.1\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.22\",\"target\":\"cfg(unix)\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.4\"},{\"features\":[\"into_bits\"],\"name\":\"packed_simd\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"rand_chacha\",\"req\":\"^0.2.1\",\"target\":\"cfg(not(target_os = \\\"emscripten\\\"))\"},{\"name\":\"rand_core\",\"req\":\"^0.5.1\"},{\"name\":\"rand_hc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"emscripten\\\")\"},{\"kind\":\"dev\",\"name\":\"rand_hc\",\"req\":\"^0.2\"},{\"name\":\"rand_pcg\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.2\"}],\"features\":{\"alloc\":[\"rand_core/alloc\"],\"default\":[\"std\"],\"getrandom\":[\"getrandom_package\",\"rand_core/getrandom\"],\"nightly\":[\"simd_support\"],\"serde1\":[],\"simd_support\":[\"packed_simd\"],\"small_rng\":[\"rand_pcg\"],\"std\":[\"rand_core/std\",\"rand_chacha/std\",\"alloc\",\"getrandom\",\"libc\"],\"stdweb\":[\"getrandom_package/stdweb\"],\"wasm-bindgen\":[\"getrandom_package/wasm-bindgen\"]}}", "rand_0.8.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.2.1\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.22\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"rand_chacha\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"rand_core\",\"req\":\"^0.6.0\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.3.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.103\"}],\"features\":{\"alloc\":[\"rand_core/alloc\"],\"default\":[\"std\",\"std_rng\"],\"getrandom\":[\"rand_core/getrandom\"],\"log\":[],\"min_const_gen\":[],\"nightly\":[],\"serde1\":[\"serde\",\"rand_core/serde1\"],\"small_rng\":[],\"std\":[\"rand_core/std\",\"rand_chacha/std\",\"alloc\",\"getrandom\",\"libc\"],\"std_rng\":[\"rand_chacha\"]}}", "rand_0.9.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.2.1\"},{\"default_features\":false,\"name\":\"rand_chacha\",\"optional\":true,\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.7\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"}],\"features\":{\"alloc\":[],\"default\":[\"std\",\"std_rng\",\"os_rng\",\"small_rng\",\"thread_rng\"],\"log\":[],\"nightly\":[],\"os_rng\":[\"rand_core/os_rng\"],\"serde\":[\"dep:serde\",\"rand_core/serde\"],\"simd_support\":[],\"small_rng\":[],\"std\":[\"rand_core/std\",\"rand_chacha?/std\",\"alloc\"],\"std_rng\":[\"dep:rand_chacha\"],\"thread_rng\":[\"std\",\"std_rng\",\"os_rng\"],\"unbiased\":[]}}", - "rand_chacha_0.2.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"ppv-lite86\",\"req\":\"^0.2.6\"},{\"name\":\"rand_core\",\"req\":\"^0.5\"}],\"features\":{\"default\":[\"std\",\"simd\"],\"simd\":[],\"std\":[\"ppv-lite86/std\"]}}", "rand_chacha_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"ppv-lite86\",\"req\":\"^0.2.8\"},{\"name\":\"rand_core\",\"req\":\"^0.6.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"serde1\":[\"serde\"],\"simd\":[],\"std\":[\"ppv-lite86/std\"]}}", "rand_chacha_0.9.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"ppv-lite86\",\"req\":\"^0.2.14\"},{\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"features\":[\"os_rng\"],\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"os_rng\":[\"rand_core/os_rng\"],\"serde\":[\"dep:serde\"],\"std\":[\"ppv-lite86/std\",\"rand_core/std\"]}}", "rand_core_0.10.1": "{\"dependencies\":[],\"features\":{}}", - "rand_core_0.5.1": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"serde1\":[\"serde\"],\"std\":[\"alloc\",\"getrandom\",\"getrandom/std\"]}}", "rand_core_0.6.4": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"serde1\":[\"serde\"],\"std\":[\"alloc\",\"getrandom\",\"getrandom/std\"]}}", "rand_core_0.9.3": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"os_rng\":[\"dep:getrandom\"],\"serde\":[\"dep:serde\"],\"std\":[\"getrandom?/std\"]}}", - "rand_hc_0.2.0": "{\"dependencies\":[{\"name\":\"rand_core\",\"req\":\"^0.5\"}],\"features\":{}}", "redis-protocol_6.0.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"bytes-utils\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"cookie-factory\",\"req\":\"=0.3.2\"},{\"name\":\"crc16\",\"req\":\"^0.4\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.14\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.12\"},{\"name\":\"libm\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"nom\",\"req\":\"^7.1\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.36\"},{\"features\":[\"codec\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7\"}],\"features\":{\"alloc\":[\"nom/alloc\"],\"bytes\":[\"dep:bytes\",\"bytes-utils\"],\"codec\":[\"tokio-util\",\"bytes\"],\"convert\":[],\"decode-logs\":[],\"default\":[\"std\",\"resp2\",\"resp3\"],\"index-map\":[\"indexmap\"],\"resp2\":[],\"resp3\":[],\"std\":[\"cookie-factory/default\",\"nom/default\"]}}", "redis-test_1.0.0": "{\"dependencies\":[{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"redis\",\"req\":\"^1\"},{\"features\":[\"aio\",\"tokio-comp\"],\"kind\":\"dev\",\"name\":\"redis\",\"req\":\"^1\"},{\"name\":\"socket2\",\"req\":\"^0.6\"},{\"name\":\"tempfile\",\"req\":\"^3.23.0\"},{\"features\":[\"rt\",\"macros\",\"rt-multi-thread\",\"test-util\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{\"aio\":[\"futures\",\"redis/aio\"]}}", "redis_1.0.0": "{\"dependencies\":[{\"name\":\"ahash\",\"optional\":true,\"req\":\"^0.8.11\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"name\":\"arc-swap\",\"optional\":true,\"req\":\"^1.7.1\"},{\"name\":\"arcstr\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"assert_approx_eq\",\"req\":\"^1.0\"},{\"name\":\"async-io\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"async-native-tls\",\"optional\":true,\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"backon\",\"optional\":true,\"req\":\"^1.6.0\"},{\"name\":\"bb8\",\"optional\":true,\"req\":\"^0.9.1\"},{\"name\":\"bigdecimal\",\"optional\":true,\"req\":\"^0.4.9\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"cfg-if\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"combine\",\"req\":\"^4.6\"},{\"name\":\"crc16\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"futures-rustls\",\"optional\":true,\"req\":\"^0.26\"},{\"kind\":\"dev\",\"name\":\"futures-time\",\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"std\",\"sink\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.31\"},{\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.16\"},{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"lru\",\"optional\":true,\"req\":\"^0.16\"},{\"name\":\"native-tls\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.6\"},{\"features\":[\"tokio\",\"quickcheck1\"],\"kind\":\"dev\",\"name\":\"partial-io\",\"req\":\"^0.5\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"name\":\"r2d2\",\"optional\":true,\"req\":\"^0.8.10\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26\"},{\"name\":\"rust_decimal\",\"optional\":true,\"req\":\"^1.39.0\"},{\"default_features\":false,\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23\"},{\"features\":[\"ring\"],\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"ryu\",\"req\":\"^1.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.219\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.145\"},{\"name\":\"sha1_smol\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"smol\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"smol-timeout\",\"optional\":true,\"req\":\"^0.6\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"req\":\"^0.6\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.23.0\"},{\"features\":[\"rt\",\"net\",\"time\",\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt\",\"macros\",\"rt-multi-thread\",\"test-util\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-native-tls\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26\"},{\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"url\",\"req\":\"^2.5\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.18.1\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"xxh3\"],\"name\":\"xxhash-rust\",\"req\":\"^0.8\"}],\"features\":{\"acl\":[],\"aio\":[\"bytes\",\"dep:pin-project-lite\",\"dep:futures-util\",\"dep:tokio\",\"dep:tokio-util\",\"tokio-util/codec\",\"combine/tokio\",\"dep:cfg-if\"],\"bb8\":[\"dep:bb8\"],\"cache-aio\":[\"aio\",\"dep:lru\"],\"cluster\":[\"dep:crc16\",\"dep:rand\"],\"cluster-async\":[\"aio\",\"cluster\",\"dep:log\"],\"connection-manager\":[\"dep:arc-swap\",\"dep:futures-channel\",\"aio\",\"dep:backon\"],\"default\":[\"acl\",\"streams\",\"geospatial\",\"script\",\"num-bigint\"],\"geospatial\":[],\"json\":[\"dep:serde\",\"serde/derive\",\"dep:serde_json\"],\"num-bigint\":[\"dep:num-bigint\"],\"r2d2\":[\"dep:r2d2\"],\"script\":[\"dep:sha1_smol\"],\"sentinel\":[\"dep:rand\"],\"smol-comp\":[\"aio\",\"dep:smol\",\"dep:smol-timeout\",\"dep:async-io\"],\"smol-native-tls-comp\":[\"smol-comp\",\"dep:async-native-tls\",\"tls-native-tls\"],\"smol-rustls-comp\":[\"smol-comp\",\"dep:futures-rustls\",\"tls-rustls\"],\"streams\":[],\"tls-native-tls\":[\"dep:native-tls\"],\"tls-rustls\":[\"dep:rustls\",\"rustls/std\",\"dep:rustls-native-certs\"],\"tls-rustls-insecure\":[\"tls-rustls\"],\"tls-rustls-webpki-roots\":[\"tls-rustls\",\"dep:webpki-roots\"],\"tokio-comp\":[\"aio\",\"tokio/net\"],\"tokio-native-tls-comp\":[\"tokio-comp\",\"tls-native-tls\",\"dep:tokio-native-tls\"],\"tokio-rustls-comp\":[\"tokio-comp\",\"tls-rustls\",\"dep:tokio-rustls\"],\"vector-sets\":[\"dep:serde\",\"serde/derive\",\"dep:serde_json\"]}}", @@ -1188,8 +1171,7 @@ "regex_1.12.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aho-corasick\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"atty\",\"humantime\",\"termcolor\"],\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.9.3\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.6.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"features\":[\"alloc\",\"syntax\",\"meta\",\"nfa-pikevm\"],\"name\":\"regex-automata\",\"req\":\"^0.4.12\"},{\"default_features\":false,\"name\":\"regex-syntax\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"regex-test\",\"req\":\"^0.1.0\"}],\"features\":{\"default\":[\"std\",\"perf\",\"unicode\",\"regex-syntax/default\"],\"logging\":[\"aho-corasick?/logging\",\"memchr?/logging\",\"regex-automata/logging\"],\"pattern\":[],\"perf\":[\"perf-cache\",\"perf-dfa\",\"perf-onepass\",\"perf-backtrack\",\"perf-inline\",\"perf-literal\"],\"perf-backtrack\":[\"regex-automata/nfa-backtrack\"],\"perf-cache\":[],\"perf-dfa\":[\"regex-automata/hybrid\"],\"perf-dfa-full\":[\"regex-automata/dfa-build\",\"regex-automata/dfa-search\"],\"perf-inline\":[\"regex-automata/perf-inline\"],\"perf-literal\":[\"dep:aho-corasick\",\"dep:memchr\",\"regex-automata/perf-literal\"],\"perf-onepass\":[\"regex-automata/dfa-onepass\"],\"std\":[\"aho-corasick?/std\",\"memchr?/std\",\"regex-automata/std\",\"regex-syntax/std\"],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\",\"regex-automata/unicode\",\"regex-syntax/unicode\"],\"unicode-age\":[\"regex-automata/unicode-age\",\"regex-syntax/unicode-age\"],\"unicode-bool\":[\"regex-automata/unicode-bool\",\"regex-syntax/unicode-bool\"],\"unicode-case\":[\"regex-automata/unicode-case\",\"regex-syntax/unicode-case\"],\"unicode-gencat\":[\"regex-automata/unicode-gencat\",\"regex-syntax/unicode-gencat\"],\"unicode-perl\":[\"regex-automata/unicode-perl\",\"regex-automata/unicode-word-boundary\",\"regex-syntax/unicode-perl\"],\"unicode-script\":[\"regex-automata/unicode-script\",\"regex-syntax/unicode-script\"],\"unicode-segment\":[\"regex-automata/unicode-segment\",\"regex-syntax/unicode-segment\"],\"unstable\":[\"pattern\"],\"use_std\":[\"std\"]}}", "regex_1.12.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aho-corasick\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"atty\",\"humantime\",\"termcolor\"],\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.9.3\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.6.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"features\":[\"alloc\",\"syntax\",\"meta\",\"nfa-pikevm\"],\"name\":\"regex-automata\",\"req\":\"^0.4.12\"},{\"default_features\":false,\"name\":\"regex-syntax\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"regex-test\",\"req\":\"^0.1.0\"}],\"features\":{\"default\":[\"std\",\"perf\",\"unicode\",\"regex-syntax/default\"],\"logging\":[\"aho-corasick?/logging\",\"memchr?/logging\",\"regex-automata/logging\"],\"pattern\":[],\"perf\":[\"perf-cache\",\"perf-dfa\",\"perf-onepass\",\"perf-backtrack\",\"perf-inline\",\"perf-literal\"],\"perf-backtrack\":[\"regex-automata/nfa-backtrack\"],\"perf-cache\":[],\"perf-dfa\":[\"regex-automata/hybrid\"],\"perf-dfa-full\":[\"regex-automata/dfa-build\",\"regex-automata/dfa-search\"],\"perf-inline\":[\"regex-automata/perf-inline\"],\"perf-literal\":[\"dep:aho-corasick\",\"dep:memchr\",\"regex-automata/perf-literal\"],\"perf-onepass\":[\"regex-automata/dfa-onepass\"],\"std\":[\"aho-corasick?/std\",\"memchr?/std\",\"regex-automata/std\",\"regex-syntax/std\"],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\",\"regex-automata/unicode\",\"regex-syntax/unicode\"],\"unicode-age\":[\"regex-automata/unicode-age\",\"regex-syntax/unicode-age\"],\"unicode-bool\":[\"regex-automata/unicode-bool\",\"regex-syntax/unicode-bool\"],\"unicode-case\":[\"regex-automata/unicode-case\",\"regex-syntax/unicode-case\"],\"unicode-gencat\":[\"regex-automata/unicode-gencat\",\"regex-syntax/unicode-gencat\"],\"unicode-perl\":[\"regex-automata/unicode-perl\",\"regex-automata/unicode-word-boundary\",\"regex-syntax/unicode-perl\"],\"unicode-script\":[\"regex-automata/unicode-script\",\"regex-syntax/unicode-script\"],\"unicode-segment\":[\"regex-automata/unicode-segment\",\"regex-syntax/unicode-segment\"],\"unstable\":[\"pattern\"],\"use_std\":[\"std\"]}}", "relative-path_2.0.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.76\"},{\"kind\":\"dev\",\"name\":\"foldhash\",\"req\":\"^0.1.5\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.160\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.160\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"default\":[\"std\",\"alloc\"],\"serde\":[\"dep:serde\"],\"std\":[]}}", - "reqwest-middleware_0.4.2": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.51\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"reqwest\",\"req\":\"^0.12.0\"},{\"features\":[\"rustls-tls\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12.0\"},{\"name\":\"serde\",\"req\":\"^1.0.106\"},{\"name\":\"thiserror\",\"req\":\"^1.0.21\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"name\":\"tower-service\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"wiremock\",\"req\":\"^0.6.0\"}],\"features\":{\"charset\":[\"reqwest/charset\"],\"http2\":[\"reqwest/http2\"],\"json\":[\"reqwest/json\"],\"multipart\":[\"reqwest/multipart\"],\"rustls-tls\":[\"reqwest/rustls-tls\"]}}", - "reqwest_0.12.24": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"tokio\"],\"name\":\"async-compression\",\"optional\":true,\"req\":\"^0.4.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"brotli_crate\",\"package\":\"brotli\",\"req\":\"^8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"cookie_crate\",\"optional\":true,\"package\":\"cookie\",\"req\":\"^0.18.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"cookie_store\",\"optional\":true,\"req\":\"^0.21.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0.13\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.28\"},{\"default_features\":false,\"features\":[\"std\",\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.28\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h3\",\"optional\":true,\"req\":\"^0.0.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h3-quinn\",\"optional\":true,\"req\":\"^0.0.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"tokio\"],\"name\":\"hickory-resolver\",\"optional\":true,\"req\":\"^0.25\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body\",\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"client\"],\"name\":\"hyper\",\"req\":\"^1.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"http1\",\"http2\",\"client\",\"server\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"http1\",\"tls12\"],\"name\":\"hyper-rustls\",\"optional\":true,\"req\":\"^0.27.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"hyper-tls\",\"optional\":true,\"req\":\"^0.6\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"client\",\"client-legacy\",\"client-proxy\",\"tokio\"],\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"http2\",\"client\",\"client-legacy\",\"server-auto\",\"server-graceful\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"js-sys\",\"req\":\"^0.3.77\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0\"},{\"name\":\"log\",\"req\":\"^0.4.17\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.16\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0\"},{\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\",\"req\":\"^0.2.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.18\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"rustls\",\"runtime-tokio\"],\"name\":\"quinn\",\"optional\":true,\"req\":\"^0.11.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"std\"],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"^1.9.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"serde_json\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_urlencoded\",\"req\":\"^0.7.1\"},{\"features\":[\"futures\"],\"name\":\"sync_wrapper\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"net\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tokio-native-tls\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"codec\",\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.9\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"retry\",\"timeout\",\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"default_features\":false,\"features\":[\"follow-redirect\"],\"name\":\"tower-http\",\"req\":\"^0.6.5\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tower-service\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"url\",\"req\":\"^2.4\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"serde-serialize\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.18\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"wasm-streams\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"AbortController\",\"AbortSignal\",\"Headers\",\"Request\",\"RequestInit\",\"RequestMode\",\"Response\",\"Window\",\"FormData\",\"Blob\",\"BlobPropertyBag\",\"ServiceWorkerGlobalScope\",\"RequestCredentials\",\"File\",\"ReadableStream\",\"RequestCache\"],\"name\":\"web-sys\",\"req\":\"^0.3.28\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"zstd_crate\",\"package\":\"zstd\",\"req\":\"^0.13\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"}],\"features\":{\"__rustls\":[\"dep:hyper-rustls\",\"dep:tokio-rustls\",\"dep:rustls\",\"__tls\"],\"__rustls-ring\":[\"hyper-rustls?/ring\",\"tokio-rustls?/ring\",\"rustls?/ring\",\"quinn?/ring\"],\"__tls\":[\"dep:rustls-pki-types\",\"tokio/io-util\"],\"blocking\":[\"dep:futures-channel\",\"futures-channel?/sink\",\"dep:futures-util\",\"futures-util?/io\",\"futures-util?/sink\",\"tokio/sync\"],\"brotli\":[\"dep:async-compression\",\"async-compression?/brotli\",\"dep:futures-util\",\"dep:tokio-util\"],\"charset\":[\"dep:encoding_rs\",\"dep:mime\"],\"cookies\":[\"dep:cookie_crate\",\"dep:cookie_store\"],\"default\":[\"default-tls\",\"charset\",\"http2\",\"system-proxy\"],\"default-tls\":[\"dep:hyper-tls\",\"dep:native-tls-crate\",\"__tls\",\"dep:tokio-native-tls\"],\"deflate\":[\"dep:async-compression\",\"async-compression?/zlib\",\"dep:futures-util\",\"dep:tokio-util\"],\"gzip\":[\"dep:async-compression\",\"async-compression?/gzip\",\"dep:futures-util\",\"dep:tokio-util\"],\"hickory-dns\":[\"dep:hickory-resolver\",\"dep:once_cell\"],\"http2\":[\"h2\",\"hyper/http2\",\"hyper-util/http2\",\"hyper-rustls?/http2\"],\"http3\":[\"rustls-tls-manual-roots\",\"dep:h3\",\"dep:h3-quinn\",\"dep:quinn\",\"tokio/macros\"],\"json\":[\"dep:serde_json\"],\"macos-system-configuration\":[\"system-proxy\"],\"multipart\":[\"dep:mime_guess\",\"dep:futures-util\"],\"native-tls\":[\"default-tls\"],\"native-tls-alpn\":[\"native-tls\",\"native-tls-crate?/alpn\",\"hyper-tls?/alpn\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate?/vendored\"],\"rustls-tls\":[\"rustls-tls-webpki-roots\"],\"rustls-tls-manual-roots\":[\"rustls-tls-manual-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-manual-roots-no-provider\":[\"__rustls\"],\"rustls-tls-native-roots\":[\"rustls-tls-native-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-native-roots-no-provider\":[\"dep:rustls-native-certs\",\"hyper-rustls?/native-tokio\",\"__rustls\"],\"rustls-tls-no-provider\":[\"rustls-tls-manual-roots-no-provider\"],\"rustls-tls-webpki-roots\":[\"rustls-tls-webpki-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-webpki-roots-no-provider\":[\"dep:webpki-roots\",\"hyper-rustls?/webpki-tokio\",\"__rustls\"],\"socks\":[],\"stream\":[\"tokio/fs\",\"dep:futures-util\",\"dep:tokio-util\",\"dep:wasm-streams\"],\"system-proxy\":[\"hyper-util/client-proxy-system\"],\"trust-dns\":[],\"zstd\":[\"dep:async-compression\",\"async-compression?/zstd\",\"dep:futures-util\",\"dep:tokio-util\"]}}", + "reqwest-middleware_0.5.2": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.51\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"reqwest\",\"req\":\"^0.13.1\"},{\"features\":[\"rustls\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.13.1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.106\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"name\":\"tower-service\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"wiremock\",\"req\":\"^0.6.0\"}],\"features\":{\"charset\":[\"reqwest/charset\"],\"form\":[\"reqwest/form\",\"dep:serde\"],\"http2\":[\"reqwest/http2\"],\"json\":[\"reqwest/json\",\"dep:serde\"],\"multipart\":[\"reqwest/multipart\"],\"query\":[\"reqwest/query\",\"dep:serde\"],\"rustls\":[\"reqwest/rustls\"],\"stream\":[\"reqwest/stream\"]}}", "reqwest_0.13.4": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"brotli_crate\",\"package\":\"brotli\",\"req\":\"^8\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"cookie_crate\",\"optional\":true,\"package\":\"cookie\",\"req\":\"^0.18.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"cookie_store\",\"optional\":true,\"req\":\"^0.22.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0.13\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.28\"},{\"default_features\":false,\"features\":[\"std\",\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.28\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"h3\",\"optional\":true,\"req\":\"^0.0.8\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"h3-quinn\",\"optional\":true,\"req\":\"^0.0.10\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"features\":[\"tokio\"],\"name\":\"hickory-resolver\",\"optional\":true,\"req\":\"^0.26\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body\",\"req\":\"^1\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.2\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"features\":[\"http1\",\"client\"],\"name\":\"hyper\",\"req\":\"^1.1\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"http1\",\"http2\",\"client\",\"server\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"http1\",\"tls12\"],\"name\":\"hyper-rustls\",\"optional\":true,\"req\":\"^0.27.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"hyper-tls\",\"optional\":true,\"req\":\"^0.6\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"features\":[\"http1\",\"client\",\"client-legacy\",\"client-proxy\",\"tokio\"],\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"features\":[\"http1\",\"http2\",\"client\",\"client-legacy\",\"server-auto\",\"server-graceful\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"js-sys\",\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0\"},{\"name\":\"log\",\"req\":\"^0.4.17\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.16\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0\"},{\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\",\"req\":\"^0.2.16\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.18\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"runtime-tokio\"],\"name\":\"quinn\",\"optional\":true,\"req\":\"^0.11.1\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.4\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"features\":[\"std\"],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"^1.9.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\">=0.6.0, <0.8.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_urlencoded\",\"optional\":true,\"req\":\"^0.7.1\"},{\"features\":[\"futures\"],\"name\":\"sync_wrapper\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"net\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"tokio-native-tls\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.9\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"retry\",\"timeout\",\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"default_features\":false,\"features\":[\"follow-redirect\"],\"name\":\"tower-http\",\"req\":\"^0.6.8\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"tower-service\",\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"name\":\"url\",\"req\":\"^2.4\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"features\":[\"serde-serialize\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.18\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"name\":\"wasm-streams\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"features\":[\"AbortController\",\"AbortSignal\",\"Headers\",\"Request\",\"RequestInit\",\"RequestMode\",\"Response\",\"Window\",\"FormData\",\"Blob\",\"BlobPropertyBag\",\"ServiceWorkerGlobalScope\",\"RequestCredentials\",\"File\",\"ReadableStream\",\"RequestCache\"],\"name\":\"web-sys\",\"req\":\"^0.3.28\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"zstd_crate\",\"package\":\"zstd\",\"req\":\"^0.13\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"}],\"features\":{\"__native-tls\":[\"dep:hyper-tls\",\"dep:native-tls-crate\",\"__tls\",\"dep:tokio-native-tls\"],\"__native-tls-alpn\":[\"native-tls-crate?/alpn\",\"hyper-tls?/alpn\"],\"__rustls\":[\"dep:hyper-rustls\",\"dep:tokio-rustls\",\"dep:rustls\",\"__tls\"],\"__rustls-aws-lc-rs\":[\"hyper-rustls?/aws-lc-rs\",\"tokio-rustls?/aws-lc-rs\",\"rustls?/aws-lc-rs\",\"quinn?/rustls-aws-lc-rs\"],\"__tls\":[\"dep:rustls-pki-types\",\"tokio/io-util\"],\"blocking\":[\"dep:futures-channel\",\"futures-channel?/sink\",\"dep:futures-util\",\"futures-util?/io\",\"futures-util?/sink\",\"tokio/sync\"],\"brotli\":[\"tower-http/decompression-br\"],\"charset\":[\"dep:encoding_rs\",\"dep:mime\"],\"cookies\":[\"dep:cookie_crate\",\"dep:cookie_store\"],\"default\":[\"default-tls\",\"charset\",\"http2\",\"system-proxy\"],\"default-tls\":[\"rustls\"],\"deflate\":[\"tower-http/decompression-deflate\"],\"form\":[\"dep:serde\",\"dep:serde_urlencoded\"],\"gzip\":[\"tower-http/decompression-gzip\"],\"hickory-dns\":[\"dep:hickory-resolver\",\"dep:once_cell\"],\"http2\":[\"dep:h2\",\"hyper/http2\",\"hyper-util/http2\",\"hyper-rustls?/http2\"],\"http3\":[\"rustls\",\"dep:h3\",\"dep:h3-quinn\",\"dep:quinn\",\"tokio/macros\"],\"json\":[\"dep:serde\",\"dep:serde_json\"],\"multipart\":[\"dep:mime_guess\",\"dep:futures-util\"],\"native-tls\":[\"__native-tls\",\"__native-tls-alpn\"],\"native-tls-no-alpn\":[\"__native-tls\"],\"native-tls-vendored\":[\"__native-tls\",\"native-tls-crate?/vendored\",\"__native-tls-alpn\"],\"native-tls-vendored-no-alpn\":[\"__native-tls\",\"native-tls-crate?/vendored\"],\"query\":[\"dep:serde\",\"dep:serde_urlencoded\"],\"rustls\":[\"__rustls-aws-lc-rs\",\"dep:rustls-platform-verifier\",\"__rustls\"],\"rustls-no-provider\":[\"dep:rustls-platform-verifier\",\"__rustls\"],\"socks\":[],\"stream\":[\"tokio/fs\",\"dep:futures-util\",\"dep:tokio-util\",\"dep:wasm-streams\"],\"system-proxy\":[\"hyper-util/client-proxy-system\"],\"zstd\":[\"tower-http/decompression-zstd\"]}}", "resolv-conf_0.7.6": "{\"dependencies\":[],\"features\":{\"system\":[]}}", "rfc6979_0.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"reset\"],\"name\":\"hmac\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2\"}],\"features\":{}}", @@ -1198,7 +1180,6 @@ "roxmltree_0.14.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.5\"},{\"name\":\"xmlparser\",\"req\":\"^0.13.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "rsa_0.9.10": "{\"dependencies\":[{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"base64ct\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"const-oid\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"alloc\",\"oid\"],\"name\":\"digest\",\"req\":\"^0.10.5\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4.1\"},{\"default_features\":false,\"features\":[\"i128\",\"prime\",\"zeroize\"],\"name\":\"num-bigint\",\"package\":\"num-bigint-dig\",\"req\":\"^0.8.6\"},{\"default_features\":false,\"name\":\"num-integer\",\"req\":\"^0.1.39\"},{\"default_features\":false,\"features\":[\"libm\"],\"name\":\"num-traits\",\"req\":\"^0.2.9\"},{\"default_features\":false,\"features\":[\"alloc\",\"pkcs8\"],\"name\":\"pkcs1\",\"req\":\"^0.7.5\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"pkcs8\",\"req\":\"^0.10.2\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand_chacha\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6.4\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.184\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.89\"},{\"default_features\":false,\"features\":[\"oid\"],\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10.5\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.10.5\"},{\"default_features\":false,\"features\":[\"oid\"],\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.6\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10.6\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha3\",\"req\":\"^0.10.7\"},{\"default_features\":false,\"features\":[\"alloc\",\"digest\",\"rand_core\"],\"name\":\"signature\",\"req\":\">2.0, <2.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"spki\",\"req\":\"^0.7.3\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.1.1\"},{\"features\":[\"alloc\"],\"name\":\"zeroize\",\"req\":\"^1.5\"}],\"features\":{\"default\":[\"std\",\"pem\",\"u64_digit\"],\"getrandom\":[\"rand_core/getrandom\"],\"hazmat\":[],\"nightly\":[\"num-bigint/nightly\"],\"pem\":[\"pkcs1/pem\",\"pkcs8/pem\"],\"pkcs5\":[\"pkcs8/encryption\"],\"serde\":[\"dep:serde\",\"num-bigint/serde\"],\"std\":[\"digest/std\",\"pkcs1/std\",\"pkcs8/std\",\"rand_core/std\",\"signature/std\"],\"u64_digit\":[\"num-bigint/u64_digit\"]}}", "rust_decimal_1.39.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"derive\",\"unstable__schema\"],\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.1.1\"},{\"default_features\":false,\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"csv\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"diesel\",\"optional\":true,\"req\":\"^2.2.3\"},{\"default_features\":false,\"features\":[\"mysql\",\"postgres\"],\"kind\":\"dev\",\"name\":\"diesel\",\"req\":\"^2.2.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"ndarray\",\"optional\":true,\"req\":\"^0.15.6\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"postgres\",\"req\":\"^0.19\"},{\"default_features\":false,\"name\":\"postgres-types\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"proptest\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"getrandom\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rand-0_9\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"thread_rng\"],\"kind\":\"dev\",\"name\":\"rand-0_9\",\"package\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"size_32\",\"std\"],\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.42\"},{\"kind\":\"dev\",\"name\":\"rkyv-0_8\",\"package\":\"rkyv\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rocket\",\"optional\":true,\"req\":\"^0.5.0-rc.3\"},{\"default_features\":false,\"name\":\"rust_decimal_macros\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tokio-postgres\",\"optional\":true,\"req\":\"^0.7\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"tokio-postgres\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"html_root_url_updated\",\"markdown_deps_updated\"],\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.9\"}],\"features\":{\"align16\":[],\"borsh\":[\"dep:borsh\",\"std\"],\"c-repr\":[],\"db-diesel-mysql\":[\"diesel/mysql_backend\",\"std\"],\"db-diesel-postgres\":[\"diesel/postgres_backend\",\"std\"],\"db-diesel2-mysql\":[\"db-diesel-mysql\"],\"db-diesel2-postgres\":[\"db-diesel-postgres\"],\"db-postgres\":[\"dep:bytes\",\"dep:postgres-types\",\"std\"],\"db-tokio-postgres\":[\"dep:bytes\",\"dep:postgres-types\",\"std\"],\"default\":[\"serde\",\"std\"],\"legacy-ops\":[],\"macros\":[\"dep:rust_decimal_macros\"],\"maths\":[],\"maths-nopanic\":[\"maths\"],\"ndarray\":[\"dep:ndarray\"],\"proptest\":[\"dep:proptest\"],\"rand\":[\"dep:rand\"],\"rkyv\":[\"dep:rkyv\"],\"rkyv-safe\":[\"rkyv/validation\"],\"rocket-traits\":[\"dep:rocket\",\"std\"],\"rust-fuzz\":[\"dep:arbitrary\"],\"serde\":[\"dep:serde\"],\"serde-arbitrary-precision\":[\"serde-with-arbitrary-precision\"],\"serde-bincode\":[\"serde-str\"],\"serde-float\":[\"serde-with-float\"],\"serde-str\":[\"serde-with-str\"],\"serde-with-arbitrary-precision\":[\"serde\",\"serde_json/arbitrary_precision\",\"serde_json/std\"],\"serde-with-float\":[\"serde\"],\"serde-with-str\":[\"serde\"],\"std\":[\"arrayvec/std\",\"borsh?/std\",\"bytes?/std\",\"rand?/std\",\"rkyv?/std\",\"serde?/std\",\"serde_json?/std\"],\"tokio-pg\":[\"db-tokio-postgres\"]}}", - "rustc-hash_2.1.1": "{\"dependencies\":[{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"}],\"features\":{\"default\":[\"std\"],\"nightly\":[],\"rand\":[\"dep:rand\",\"std\"],\"std\":[]}}", "rustc_version_0.4.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"semver\",\"req\":\"^1.0\"}],\"features\":{}}", "rustc_version_runtime_0.3.0": "{\"dependencies\":[{\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"name\":\"semver\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"semver\",\"req\":\"^1.0\"}],\"features\":{}}", "rustix_1.1.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitflags\",\"req\":\"^2.4.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\",\"target\":\"cfg(all(criterion, not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.171\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.171\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(windows)\"},{\"default_features\":false,\"name\":\"libc_errno\",\"optional\":true,\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\"},{\"default_features\":false,\"features\":[\"general\",\"ioctl\",\"no_std\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.11.0\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\"), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"features\":[\"auxvec\",\"general\",\"errno\",\"ioctl\",\"no_std\",\"elf\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.11.0\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.20.3\",\"target\":\"cfg(windows)\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.5.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"all-apis\":[\"event\",\"fs\",\"io_uring\",\"mm\",\"mount\",\"net\",\"param\",\"pipe\",\"process\",\"pty\",\"rand\",\"runtime\",\"shm\",\"stdio\",\"system\",\"termios\",\"thread\",\"time\"],\"alloc\":[],\"default\":[\"std\"],\"event\":[],\"fs\":[],\"io_uring\":[\"event\",\"fs\",\"net\",\"thread\",\"linux-raw-sys/io_uring\"],\"linux_4_11\":[],\"linux_5_1\":[\"linux_4_11\"],\"linux_5_11\":[\"linux_5_1\"],\"linux_latest\":[\"linux_5_11\"],\"mm\":[],\"mount\":[],\"net\":[\"linux-raw-sys/net\",\"linux-raw-sys/netlink\",\"linux-raw-sys/if_ether\",\"linux-raw-sys/xdp\"],\"param\":[],\"pipe\":[],\"process\":[\"linux-raw-sys/prctl\"],\"pty\":[\"fs\"],\"rand\":[],\"runtime\":[\"linux-raw-sys/prctl\"],\"rustc-dep-of-std\":[\"core\",\"rustc-std-workspace-alloc\",\"linux-raw-sys/rustc-dep-of-std\",\"bitflags/rustc-dep-of-std\"],\"shm\":[\"fs\"],\"std\":[\"bitflags/std\",\"alloc\",\"libc?/std\",\"libc_errno?/std\"],\"stdio\":[],\"system\":[\"linux-raw-sys/system\"],\"termios\":[],\"thread\":[\"linux-raw-sys/prctl\"],\"time\":[],\"try_close\":[],\"use-explicitly-provided-auxv\":[],\"use-libc\":[\"libc_errno\",\"libc\"],\"use-libc-auxv\":[]}}", @@ -1234,7 +1215,6 @@ "serde_json5_0.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"matches\",\"req\":\"^0.1.8\"},{\"name\":\"pest\",\"req\":\"^2.0\"},{\"name\":\"pest_derive\",\"req\":\"^2.0\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{}}", "serde_json_1.0.149": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.11\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2.3\"},{\"kind\":\"dev\",\"name\":\"indoc\",\"req\":\"^2.0.2\"},{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"kind\":\"dev\",\"name\":\"serde_bytes\",\"req\":\"^0.11.10\"},{\"default_features\":false,\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_stacker\",\"req\":\"^0.1.8\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"},{\"name\":\"zmij\",\"req\":\"^1.0\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"arbitrary_precision\":[],\"default\":[\"std\"],\"float_roundtrip\":[],\"preserve_order\":[\"indexmap\",\"std\"],\"raw_value\":[],\"std\":[\"memchr/std\",\"serde_core/std\"],\"unbounded_depth\":[]}}", "serde_json_1.0.150": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.11\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2.3\"},{\"kind\":\"dev\",\"name\":\"indoc\",\"req\":\"^2.0.2\"},{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"kind\":\"dev\",\"name\":\"serde_bytes\",\"req\":\"^0.11.10\"},{\"default_features\":false,\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_stacker\",\"req\":\"^0.1.8\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"},{\"name\":\"zmij\",\"req\":\"^1.0\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"arbitrary_precision\":[],\"default\":[\"std\"],\"float_roundtrip\":[],\"preserve_order\":[\"indexmap\",\"std\"],\"raw_value\":[],\"std\":[\"memchr/std\",\"serde_core/std\"],\"unbounded_depth\":[]}}", - "serde_qs_0.8.5": "{\"dependencies\":[{\"default_features\":false,\"name\":\"actix-web\",\"optional\":true,\"package\":\"actix-web\",\"req\":\"^3.3\"},{\"default_features\":false,\"name\":\"actix-web2\",\"optional\":true,\"package\":\"actix-web\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"csv\",\"req\":\"^1.1\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_urlencoded\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_with\",\"req\":\"^1.10\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"warp-framework\",\"optional\":true,\"package\":\"warp\",\"req\":\"^0.3\"}],\"features\":{\"actix\":[\"actix-web\",\"futures\"],\"actix2\":[\"actix-web2\",\"futures\"],\"default\":[],\"warp\":[\"futures\",\"tracing\",\"warp-framework\"]}}", "serde_test_1.0.177": "{\"dependencies\":[{\"name\":\"serde\",\"req\":\"^1.0.69\"},{\"features\":[\"rc\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{}}", "serde_urlencoded_0.7.1": "{\"dependencies\":[{\"name\":\"form_urlencoded\",\"req\":\"^1\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"name\":\"serde\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{}}", "serde_with_3.15.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"chrono_0_4\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4.20\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"expect-test\",\"req\":\"^1.5.1\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_14\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.14.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_15\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_16\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.16.0\"},{\"default_features\":false,\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4.3\"},{\"default_features\":false,\"features\":[\"serde-1\"],\"name\":\"indexmap_1\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^1.8\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"indexmap_2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"resolve-file\"],\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.33.0\"},{\"kind\":\"dev\",\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.12.1\"},{\"kind\":\"dev\",\"name\":\"rmp-serde\",\"req\":\"^1.3.0\"},{\"kind\":\"dev\",\"name\":\"ron\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"name\":\"schemars_0_8\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^0.8.16\"},{\"kind\":\"dev\",\"name\":\"schemars_0_8\",\"package\":\"schemars\",\"req\":\"^0.8.16\"},{\"default_features\":false,\"name\":\"schemars_0_9\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"schemars_0_9\",\"package\":\"schemars\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"schemars_1\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"schemars_1\",\"package\":\"schemars\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde-xml-rs\",\"req\":\"^0.8.1\"},{\"default_features\":false,\"features\":[\"result\"],\"name\":\"serde_core\",\"req\":\"^1.0.225\"},{\"default_features\":false,\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.145\"},{\"features\":[\"preserve_order\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.124\"},{\"name\":\"serde_with_macros\",\"optional\":true,\"req\":\"=3.15.1\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"name\":\"time_0_3\",\"optional\":true,\"package\":\"time\",\"req\":\"~0.3.36\"}],\"features\":{\"alloc\":[\"serde_core/alloc\",\"base64?/alloc\",\"chrono_0_4?/alloc\",\"hex?/alloc\",\"serde_json?/alloc\",\"time_0_3?/alloc\"],\"base64\":[\"dep:base64\",\"alloc\"],\"chrono\":[\"chrono_0_4\"],\"chrono_0_4\":[\"dep:chrono_0_4\"],\"default\":[\"std\",\"macros\"],\"guide\":[\"dep:document-features\",\"macros\",\"std\"],\"hashbrown_0_14\":[\"dep:hashbrown_0_14\",\"alloc\"],\"hashbrown_0_15\":[\"dep:hashbrown_0_15\",\"alloc\"],\"hashbrown_0_16\":[\"dep:hashbrown_0_16\",\"alloc\"],\"hex\":[\"dep:hex\",\"alloc\"],\"indexmap\":[\"indexmap_1\"],\"indexmap_1\":[\"dep:indexmap_1\",\"alloc\"],\"indexmap_2\":[\"dep:indexmap_2\",\"alloc\"],\"json\":[\"dep:serde_json\",\"alloc\"],\"macros\":[\"dep:serde_with_macros\"],\"schemars_0_8\":[\"dep:schemars_0_8\",\"std\",\"serde_with_macros?/schemars_0_8\"],\"schemars_0_9\":[\"dep:schemars_0_9\",\"alloc\",\"serde_with_macros?/schemars_0_9\",\"dep:serde_json\"],\"schemars_1\":[\"dep:schemars_1\",\"alloc\",\"serde_with_macros?/schemars_1\",\"dep:serde_json\"],\"std\":[\"alloc\",\"serde_core/std\",\"chrono_0_4?/clock\",\"chrono_0_4?/std\",\"indexmap_1?/std\",\"indexmap_2?/std\",\"time_0_3?/serde-well-known\",\"time_0_3?/std\",\"schemars_0_9?/std\",\"schemars_1?/std\"],\"time_0_3\":[\"dep:time_0_3\"]}}", @@ -1327,6 +1307,9 @@ "typed-builder_0.20.1": "{\"dependencies\":[{\"name\":\"typed-builder-macro\",\"req\":\"=0.20.1\"}],\"features\":{}}", "typed-path_0.12.3": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "typenum_1.19.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"scale-info\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"const-generics\":[],\"force_unix_path_separator\":[],\"i128\":[],\"no_std\":[],\"scale_info\":[\"scale-info/derive\"],\"strict\":[]}}", + "typespec_1.0.0": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.11.1\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"features\":[\"serialize\",\"serde-types\"],\"name\":\"quick-xml\",\"optional\":true,\"req\":\"^0.39.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.149\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"name\":\"url\",\"req\":\"^2.5\"}],\"features\":{\"default\":[\"http\",\"json\"],\"http\":[],\"json\":[\"dep:serde\",\"dep:serde_json\"],\"xml\":[\"dep:serde\",\"dep:quick-xml\"]}}", + "typespec_client_core_1.0.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.11.1\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"gloo-timers\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"pin-project\",\"req\":\"^1.1\"},{\"features\":[\"sys_rng\"],\"name\":\"rand\",\"req\":\"^0.10.1\"},{\"default_features\":false,\"features\":[\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.2\"},{\"name\":\"rust_decimal\",\"optional\":true,\"req\":\"^1.40.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.149\"},{\"features\":[\"serde-well-known\",\"macros\"],\"name\":\"time\",\"req\":\"^0.3.47\"},{\"default_features\":false,\"features\":[\"macros\",\"time\",\"macros\",\"rt-multi-thread\",\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.49\"},{\"default_features\":false,\"features\":[\"macros\",\"time\",\"fs\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.49\"},{\"name\":\"tracing\",\"req\":\"^0.1.44\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.44\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"typespec\",\"req\":\"^1.0.0\"},{\"name\":\"typespec_macros\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"typespec_macros\",\"req\":\"^1.0.0\"},{\"name\":\"url\",\"req\":\"^2.5\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"req\":\"^1.20\"}],\"features\":{\"debug\":[\"typespec_macros?/debug\"],\"decimal\":[\"dep:rust_decimal\"],\"default\":[\"http\",\"json\",\"reqwest\",\"reqwest_deflate\",\"reqwest_gzip\",\"reqwest_rustls\",\"tokio\"],\"derive\":[\"dep:typespec_macros\"],\"http\":[\"typespec/http\"],\"json\":[\"dep:serde_json\",\"typespec/json\"],\"reqwest\":[\"dep:reqwest\"],\"reqwest_deflate\":[\"reqwest\",\"reqwest/deflate\"],\"reqwest_gzip\":[\"reqwest\",\"reqwest/gzip\"],\"reqwest_rustls\":[\"reqwest\",\"reqwest/rustls\"],\"test\":[],\"tokio\":[\"tokio/sync\",\"tokio/time\"],\"xml\":[\"dep:serde_json\",\"typespec/xml\"]}}", + "typespec_macros_1.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"cargo_metadata\",\"req\":\"^0.23.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.106\"},{\"name\":\"quote\",\"req\":\"^1.0.44\"},{\"name\":\"rustc_version\",\"req\":\"^0.4\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.149\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.115\"},{\"default_features\":false,\"features\":[\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.49\"}],\"features\":{\"debug\":[]}}", "ucd-trie_0.1.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "unicase_2.8.1": "{\"dependencies\":[],\"features\":{\"nightly\":[]}}", "unicode-bidi_0.3.18": "{\"dependencies\":[{\"name\":\"flame\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"flamer\",\"optional\":true,\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\">=0.8, <2.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\">=0.8, <2.0\"},{\"features\":[\"union\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\">=1.13\"}],\"features\":{\"bench_it\":[],\"default\":[\"std\",\"hardcoded-data\"],\"flame_it\":[\"flame\",\"flamer\"],\"hardcoded-data\":[],\"std\":[],\"unstable\":[],\"with_serde\":[\"serde\"]}}", @@ -1344,25 +1327,22 @@ "valuable_0.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"valuable-derive\",\"optional\":true,\"req\":\"=0.1.1\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"derive\":[\"valuable-derive\"],\"std\":[\"alloc\"]}}", "version_check_0.9.5": "{\"dependencies\":[],\"features\":{}}", "vsimd_0.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"const-str\",\"req\":\"^0.5.3\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.8\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.33\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"}],\"features\":{\"alloc\":[],\"detect\":[\"std\"],\"std\":[\"alloc\"],\"unstable\":[]}}", - "waker-fn_1.2.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2\"}],\"features\":{\"portable-atomic\":[\"portable-atomic-util\"]}}", "walkdir_2.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"same-file\",\"req\":\"^1.0.1\"},{\"name\":\"winapi-util\",\"req\":\"^0.1.1\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "want_0.3.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"tokio-executor\",\"req\":\"^0.2.0-alpha.2\"},{\"kind\":\"dev\",\"name\":\"tokio-sync\",\"req\":\"^0.2.0-alpha.2\"},{\"name\":\"try-lock\",\"req\":\"^0.2.4\"}],\"features\":{}}", "wasi_0.11.1+wasi-snapshot-preview1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"rustc-dep-of-std\":[\"core\",\"rustc-std-workspace-alloc\"],\"std\":[]}}", - "wasi_0.9.0+wasi-snapshot-preview1": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\",\"rustc-std-workspace-alloc\"],\"std\":[]}}", "wasip2_1.0.2+wasi-0.2.9": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"wit-bindgen\",\"req\":\"^0.51.0\"}],\"features\":{\"bitflags\":[\"wit-bindgen/bitflags\"],\"default\":[\"std\",\"bitflags\"],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"wit-bindgen/rustc-dep-of-std\"],\"std\":[]}}", "wasip2_1.0.3+wasi-0.2.9": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"wit-bindgen\",\"req\":\"^0.57.1\"}],\"features\":{\"bitflags\":[\"wit-bindgen/bitflags\"],\"default\":[\"std\",\"bitflags\"],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"wit-bindgen/rustc-dep-of-std\"],\"std\":[]}}", "wasip3_0.4.0+wasi-0.3.0-rc-2026-01-06": "{\"dependencies\":[{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.10.1\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.31\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^1.3.1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1.0.1\"},{\"name\":\"thiserror\",\"optional\":true,\"req\":\"^2.0.17\"},{\"default_features\":false,\"features\":[\"async\"],\"name\":\"wit-bindgen\",\"req\":\"^0.51.0\"},{\"default_features\":false,\"features\":[\"async-spawn\"],\"kind\":\"dev\",\"name\":\"wit-bindgen\",\"req\":\"^0.51.0\"}],\"features\":{\"http-compat\":[\"dep:bytes\",\"dep:http-body\",\"dep:http\",\"dep:thiserror\",\"wit-bindgen/async-spawn\"]}}", - "wasm-bindgen-backend_0.2.104": "{\"dependencies\":[{\"name\":\"bumpalo\",\"req\":\"^3.0.0\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.104\"}],\"features\":{\"extra-traits\":[\"syn/extra-traits\"]}}", - "wasm-bindgen-futures_0.4.54": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.8\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.81\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.104\"},{\"default_features\":false,\"features\":[\"MessageEvent\",\"Worker\"],\"name\":\"web-sys\",\"req\":\"=0.3.81\",\"target\":\"cfg(target_feature = \\\"atomics\\\")\"}],\"features\":{\"default\":[\"std\"],\"futures-core-03-stream\":[\"futures-core\"],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\",\"web-sys/std\"]}}", - "wasm-bindgen-macro-support_0.2.104": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"visit\",\"visit-mut\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"wasm-bindgen-backend\",\"req\":\"=0.2.104\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.104\"}],\"features\":{\"extra-traits\":[\"syn/extra-traits\"],\"strict-macro\":[]}}", - "wasm-bindgen-macro_0.2.104": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro-support\",\"req\":\"=0.2.104\"}],\"features\":{\"strict-macro\":[\"wasm-bindgen-macro-support/strict-macro\"]}}", - "wasm-bindgen-shared_0.2.104": "{\"dependencies\":[{\"name\":\"unicode-ident\",\"req\":\"^1.0.5\"}],\"features\":{}}", - "wasm-bindgen_0.2.104": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"build\",\"name\":\"rustversion-compat\",\"package\":\"rustversion\",\"req\":\"^1.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro\",\"req\":\"=0.2.104\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.104\"}],\"features\":{\"default\":[\"std\"],\"enable-interning\":[\"std\"],\"gg-alloc\":[],\"msrv\":[],\"rustversion\":[],\"serde-serialize\":[\"serde\",\"serde_json\",\"std\"],\"spans\":[],\"std\":[],\"strict-macro\":[\"wasm-bindgen-macro/strict-macro\"],\"xxx_debug_only_print_generated_code\":[]}}", + "wasm-bindgen-futures_0.4.75": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.102\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.125\"}],\"features\":{\"default\":[\"std\"],\"futures-core-03-stream\":[\"js-sys/futures-core-03-stream\"],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\"]}}", + "wasm-bindgen-macro-support_0.2.125": "{\"dependencies\":[{\"name\":\"bumpalo\",\"req\":\"^3.0.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"visit\",\"visit-mut\",\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.125\"}],\"features\":{\"extra-traits\":[\"syn/extra-traits\"],\"strict-macro\":[]}}", + "wasm-bindgen-macro_0.2.125": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro-support\",\"req\":\"=0.2.125\"}],\"features\":{\"strict-macro\":[\"wasm-bindgen-macro-support/strict-macro\"]}}", + "wasm-bindgen-shared_0.2.125": "{\"dependencies\":[{\"name\":\"unicode-ident\",\"req\":\"^1.0.5\"}],\"features\":{}}", + "wasm-bindgen_0.2.125": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\",\"target\":\"cfg(target_arch = \\\"wasm64\\\")\"},{\"kind\":\"build\",\"name\":\"rustversion-compat\",\"package\":\"rustversion\",\"req\":\"^1.0.6\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm64\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro\",\"req\":\"=0.2.125\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.125\"}],\"features\":{\"default\":[\"std\"],\"enable-interning\":[\"std\"],\"gg-alloc\":[],\"msrv\":[],\"rustversion\":[],\"serde-serialize\":[\"serde\",\"serde_json\",\"std\"],\"spans\":[],\"std\":[],\"strict-macro\":[\"wasm-bindgen-macro/strict-macro\"],\"xxx_debug_only_print_generated_code\":[]}}", "wasm-encoder_0.244.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.58\"},{\"default_features\":false,\"name\":\"leb128fmt\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.2.0\"},{\"default_features\":false,\"features\":[\"simd\",\"simd\"],\"name\":\"wasmparser\",\"optional\":true,\"req\":\"^0.244.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"wasmprinter\",\"req\":\"^0.244.0\"}],\"features\":{\"component-model\":[\"wasmparser?/component-model\"],\"default\":[\"std\",\"component-model\"],\"std\":[\"wasmparser?/std\"]}}", "wasm-metadata_0.244.0": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.58\"},{\"name\":\"auditable-serde\",\"optional\":true,\"req\":\"^0.8.0\"},{\"features\":[\"derive\"],\"name\":\"clap\",\"optional\":true,\"req\":\"^4.0.0\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"indexmap\",\"req\":\"^2.7.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.166\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0.166\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"spdx\",\"optional\":true,\"req\":\"^0.10.1\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.0.0\"},{\"default_features\":false,\"features\":[\"std\",\"component-model\"],\"name\":\"wasm-encoder\",\"req\":\"^0.244.0\"},{\"default_features\":false,\"features\":[\"simd\",\"std\",\"component-model\",\"hash-collections\"],\"name\":\"wasmparser\",\"req\":\"^0.244.0\"}],\"features\":{\"default\":[\"oci\",\"serde\"],\"oci\":[\"dep:auditable-serde\",\"dep:flate2\",\"dep:url\",\"dep:spdx\",\"dep:serde_json\",\"serde\"],\"serde\":[\"dep:serde_derive\",\"dep:serde\"]}}", - "wasm-streams_0.4.2": "{\"dependencies\":[{\"features\":[\"io\",\"sink\"],\"name\":\"futures-util\",\"req\":\"^0.3.31\"},{\"features\":[\"futures\"],\"kind\":\"dev\",\"name\":\"gloo-timers\",\"req\":\"^0.3.0\"},{\"name\":\"js-sys\",\"req\":\"^0.3.72\"},{\"kind\":\"dev\",\"name\":\"pin-project\",\"req\":\"^1\"},{\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.95\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.45\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.45\"},{\"features\":[\"AbortSignal\",\"QueuingStrategy\",\"ReadableStream\",\"ReadableStreamType\",\"ReadableWritablePair\",\"ReadableStreamByobReader\",\"ReadableStreamReaderMode\",\"ReadableStreamReadResult\",\"ReadableStreamByobRequest\",\"ReadableStreamDefaultReader\",\"ReadableByteStreamController\",\"ReadableStreamGetReaderOptions\",\"ReadableStreamDefaultController\",\"StreamPipeOptions\",\"TransformStream\",\"TransformStreamDefaultController\",\"Transformer\",\"UnderlyingSink\",\"UnderlyingSource\",\"WritableStream\",\"WritableStreamDefaultController\",\"WritableStreamDefaultWriter\"],\"name\":\"web-sys\",\"req\":\"^0.3.72\"},{\"features\":[\"console\",\"AbortSignal\",\"ErrorEvent\",\"PromiseRejectionEvent\",\"Response\",\"ReadableStream\",\"Window\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3.72\"}],\"features\":{}}", + "wasm-streams_0.5.0": "{\"dependencies\":[{\"features\":[\"io\",\"sink\"],\"name\":\"futures-util\",\"req\":\"^0.3.31\"},{\"features\":[\"futures\"],\"kind\":\"dev\",\"name\":\"gloo-timers\",\"req\":\"^0.3.0\"},{\"name\":\"js-sys\",\"req\":\"^0.3.85\"},{\"kind\":\"dev\",\"name\":\"pin-project\",\"req\":\"^1\"},{\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.108\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.58\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.58\"},{\"features\":[\"AbortSignal\",\"QueuingStrategy\",\"ReadableStream\",\"ReadableStreamType\",\"ReadableWritablePair\",\"ReadableStreamByobReader\",\"ReadableStreamReaderMode\",\"ReadableStreamReadResult\",\"ReadableStreamByobRequest\",\"ReadableStreamDefaultReader\",\"ReadableByteStreamController\",\"ReadableStreamGetReaderOptions\",\"ReadableStreamDefaultController\",\"StreamPipeOptions\",\"TransformStream\",\"TransformStreamDefaultController\",\"Transformer\",\"UnderlyingSink\",\"UnderlyingSource\",\"WritableStream\",\"WritableStreamDefaultController\",\"WritableStreamDefaultWriter\"],\"name\":\"web-sys\",\"req\":\"^0.3.85\"},{\"features\":[\"console\",\"AbortSignal\",\"ErrorEvent\",\"PromiseRejectionEvent\",\"Response\",\"ReadableStream\",\"Window\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3.85\"}],\"features\":{}}", "wasmparser_0.244.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.58\"},{\"name\":\"bitflags\",\"req\":\"^2.4.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"default_features\":false,\"features\":[\"default-hasher\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.2\"},{\"default_features\":false,\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.7.0\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.17\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.13.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.3\"},{\"default_features\":false,\"name\":\"semver\",\"optional\":true,\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.166\"}],\"features\":{\"component-model\":[\"dep:semver\"],\"default\":[\"std\",\"validate\",\"serde\",\"features\",\"component-model\",\"hash-collections\",\"simd\"],\"features\":[],\"hash-collections\":[\"dep:hashbrown\",\"dep:indexmap\"],\"prefer-btree-collections\":[],\"serde\":[\"dep:serde\",\"indexmap?/serde\",\"hashbrown?/serde\"],\"simd\":[],\"std\":[\"indexmap?/std\"],\"validate\":[]}}", - "web-sys_0.3.81": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.81\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.104\"}],\"features\":{\"AbortController\":[],\"AbortSignal\":[\"EventTarget\"],\"AddEventListenerOptions\":[],\"AesCbcParams\":[],\"AesCtrParams\":[],\"AesDerivedKeyParams\":[],\"AesGcmParams\":[],\"AesKeyAlgorithm\":[],\"AesKeyGenParams\":[],\"Algorithm\":[],\"AlignSetting\":[],\"AllowedBluetoothDevice\":[],\"AllowedUsbDevice\":[],\"AlphaOption\":[],\"AnalyserNode\":[\"AudioNode\",\"EventTarget\"],\"AnalyserOptions\":[],\"AngleInstancedArrays\":[],\"Animation\":[\"EventTarget\"],\"AnimationEffect\":[],\"AnimationEvent\":[\"Event\"],\"AnimationEventInit\":[],\"AnimationPlayState\":[],\"AnimationPlaybackEvent\":[\"Event\"],\"AnimationPlaybackEventInit\":[],\"AnimationPropertyDetails\":[],\"AnimationPropertyValueDetails\":[],\"AnimationTimeline\":[],\"AssignedNodesOptions\":[],\"AttestationConveyancePreference\":[],\"Attr\":[\"EventTarget\",\"Node\"],\"AttributeNameValue\":[],\"AudioBuffer\":[],\"AudioBufferOptions\":[],\"AudioBufferSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"AudioBufferSourceOptions\":[],\"AudioConfiguration\":[],\"AudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"AudioContextLatencyCategory\":[],\"AudioContextOptions\":[],\"AudioContextState\":[],\"AudioData\":[],\"AudioDataCopyToOptions\":[],\"AudioDataInit\":[],\"AudioDecoder\":[],\"AudioDecoderConfig\":[],\"AudioDecoderInit\":[],\"AudioDecoderSupport\":[],\"AudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"AudioEncoder\":[],\"AudioEncoderConfig\":[],\"AudioEncoderInit\":[],\"AudioEncoderSupport\":[],\"AudioListener\":[],\"AudioNode\":[\"EventTarget\"],\"AudioNodeOptions\":[],\"AudioParam\":[],\"AudioParamMap\":[],\"AudioProcessingEvent\":[\"Event\"],\"AudioSampleFormat\":[],\"AudioScheduledSourceNode\":[\"AudioNode\",\"EventTarget\"],\"AudioSinkInfo\":[],\"AudioSinkOptions\":[],\"AudioSinkType\":[],\"AudioStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"AudioTrack\":[],\"AudioTrackList\":[\"EventTarget\"],\"AudioWorklet\":[\"Worklet\"],\"AudioWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"AudioWorkletNode\":[\"AudioNode\",\"EventTarget\"],\"AudioWorkletNodeOptions\":[],\"AudioWorkletProcessor\":[],\"AuthenticationExtensionsClientInputs\":[],\"AuthenticationExtensionsClientInputsJson\":[],\"AuthenticationExtensionsClientOutputs\":[],\"AuthenticationExtensionsClientOutputsJson\":[],\"AuthenticationExtensionsDevicePublicKeyInputs\":[],\"AuthenticationExtensionsDevicePublicKeyOutputs\":[],\"AuthenticationExtensionsLargeBlobInputs\":[],\"AuthenticationExtensionsLargeBlobOutputs\":[],\"AuthenticationExtensionsPrfInputs\":[],\"AuthenticationExtensionsPrfOutputs\":[],\"AuthenticationExtensionsPrfValues\":[],\"AuthenticationResponseJson\":[],\"AuthenticatorAssertionResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAssertionResponseJson\":[],\"AuthenticatorAttachment\":[],\"AuthenticatorAttestationResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAttestationResponseJson\":[],\"AuthenticatorResponse\":[],\"AuthenticatorSelectionCriteria\":[],\"AuthenticatorTransport\":[],\"AutoKeyword\":[],\"AutocompleteInfo\":[],\"BarProp\":[],\"BaseAudioContext\":[\"EventTarget\"],\"BaseComputedKeyframe\":[],\"BaseKeyframe\":[],\"BasePropertyIndexedKeyframe\":[],\"BasicCardRequest\":[],\"BasicCardResponse\":[],\"BasicCardType\":[],\"BatteryManager\":[\"EventTarget\"],\"BeforeUnloadEvent\":[\"Event\"],\"BinaryType\":[],\"BiquadFilterNode\":[\"AudioNode\",\"EventTarget\"],\"BiquadFilterOptions\":[],\"BiquadFilterType\":[],\"Blob\":[],\"BlobEvent\":[\"Event\"],\"BlobEventInit\":[],\"BlobPropertyBag\":[],\"BlockParsingOptions\":[],\"Bluetooth\":[\"EventTarget\"],\"BluetoothAdvertisingEvent\":[\"Event\"],\"BluetoothAdvertisingEventInit\":[],\"BluetoothCharacteristicProperties\":[],\"BluetoothDataFilterInit\":[],\"BluetoothDevice\":[\"EventTarget\"],\"BluetoothLeScanFilterInit\":[],\"BluetoothManufacturerDataMap\":[],\"BluetoothPermissionDescriptor\":[],\"BluetoothPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"BluetoothPermissionStorage\":[],\"BluetoothRemoteGattCharacteristic\":[\"EventTarget\"],\"BluetoothRemoteGattDescriptor\":[],\"BluetoothRemoteGattServer\":[],\"BluetoothRemoteGattService\":[\"EventTarget\"],\"BluetoothServiceDataMap\":[],\"BluetoothUuid\":[],\"BoxQuadOptions\":[],\"BroadcastChannel\":[\"EventTarget\"],\"BrowserElementDownloadOptions\":[],\"BrowserElementExecuteScriptOptions\":[],\"BrowserFeedWriter\":[],\"BrowserFindCaseSensitivity\":[],\"BrowserFindDirection\":[],\"ByteLengthQueuingStrategy\":[],\"Cache\":[],\"CacheBatchOperation\":[],\"CacheQueryOptions\":[],\"CacheStorage\":[],\"CacheStorageNamespace\":[],\"CanvasCaptureMediaStream\":[\"EventTarget\",\"MediaStream\"],\"CanvasCaptureMediaStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"CanvasGradient\":[],\"CanvasPattern\":[],\"CanvasRenderingContext2d\":[],\"CanvasWindingRule\":[],\"CaretChangedReason\":[],\"CaretPosition\":[],\"CaretStateChangedEventInit\":[],\"CdataSection\":[\"CharacterData\",\"EventTarget\",\"Node\",\"Text\"],\"ChannelCountMode\":[],\"ChannelInterpretation\":[],\"ChannelMergerNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelMergerOptions\":[],\"ChannelSplitterNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelSplitterOptions\":[],\"CharacterData\":[\"EventTarget\",\"Node\"],\"CheckerboardReason\":[],\"CheckerboardReport\":[],\"CheckerboardReportService\":[],\"ChromeFilePropertyBag\":[],\"ChromeWorker\":[\"EventTarget\",\"Worker\"],\"Client\":[],\"ClientQueryOptions\":[],\"ClientRectsAndTexts\":[],\"ClientType\":[],\"Clients\":[],\"Clipboard\":[\"EventTarget\"],\"ClipboardEvent\":[\"Event\"],\"ClipboardEventInit\":[],\"ClipboardItem\":[],\"ClipboardItemOptions\":[],\"ClipboardPermissionDescriptor\":[],\"ClipboardUnsanitizedFormats\":[],\"CloseEvent\":[\"Event\"],\"CloseEventInit\":[],\"CodecState\":[],\"CollectedClientData\":[],\"ColorSpaceConversion\":[],\"Comment\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"CompositeOperation\":[],\"CompositionEvent\":[\"Event\",\"UiEvent\"],\"CompositionEventInit\":[],\"CompressionFormat\":[],\"CompressionStream\":[],\"ComputedEffectTiming\":[],\"ConnStatusDict\":[],\"ConnectionType\":[],\"ConsoleCounter\":[],\"ConsoleCounterError\":[],\"ConsoleEvent\":[],\"ConsoleInstance\":[],\"ConsoleInstanceOptions\":[],\"ConsoleLevel\":[],\"ConsoleLogLevel\":[],\"ConsoleProfileEvent\":[],\"ConsoleStackEntry\":[],\"ConsoleTimerError\":[],\"ConsoleTimerLogOrEnd\":[],\"ConsoleTimerStart\":[],\"ConstantSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"ConstantSourceOptions\":[],\"ConstrainBooleanParameters\":[],\"ConstrainDomStringParameters\":[],\"ConstrainDoubleRange\":[],\"ConstrainLongRange\":[],\"ContextAttributes2d\":[],\"ConvertCoordinateOptions\":[],\"ConvolverNode\":[\"AudioNode\",\"EventTarget\"],\"ConvolverOptions\":[],\"Coordinates\":[],\"CountQueuingStrategy\":[],\"Credential\":[],\"CredentialCreationOptions\":[],\"CredentialPropertiesOutput\":[],\"CredentialRequestOptions\":[],\"CredentialsContainer\":[],\"Crypto\":[],\"CryptoKey\":[],\"CryptoKeyPair\":[],\"CssAnimation\":[\"Animation\",\"EventTarget\"],\"CssBoxType\":[],\"CssConditionRule\":[\"CssGroupingRule\",\"CssRule\"],\"CssCounterStyleRule\":[\"CssRule\"],\"CssFontFaceRule\":[\"CssRule\"],\"CssFontFeatureValuesRule\":[\"CssRule\"],\"CssGroupingRule\":[\"CssRule\"],\"CssImportRule\":[\"CssRule\"],\"CssKeyframeRule\":[\"CssRule\"],\"CssKeyframesRule\":[\"CssRule\"],\"CssMediaRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssNamespaceRule\":[\"CssRule\"],\"CssPageRule\":[\"CssRule\"],\"CssPseudoElement\":[],\"CssRule\":[],\"CssRuleList\":[],\"CssStyleDeclaration\":[],\"CssStyleRule\":[\"CssRule\"],\"CssStyleSheet\":[\"StyleSheet\"],\"CssStyleSheetParsingMode\":[],\"CssSupportsRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssTransition\":[\"Animation\",\"EventTarget\"],\"CustomElementRegistry\":[],\"CustomEvent\":[\"Event\"],\"CustomEventInit\":[],\"DataTransfer\":[],\"DataTransferItem\":[],\"DataTransferItemList\":[],\"DateTimeValue\":[],\"DecoderDoctorNotification\":[],\"DecoderDoctorNotificationType\":[],\"DecompressionStream\":[],\"DedicatedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"DelayNode\":[\"AudioNode\",\"EventTarget\"],\"DelayOptions\":[],\"DeviceAcceleration\":[],\"DeviceAccelerationInit\":[],\"DeviceLightEvent\":[\"Event\"],\"DeviceLightEventInit\":[],\"DeviceMotionEvent\":[\"Event\"],\"DeviceMotionEventInit\":[],\"DeviceOrientationEvent\":[\"Event\"],\"DeviceOrientationEventInit\":[],\"DeviceProximityEvent\":[\"Event\"],\"DeviceProximityEventInit\":[],\"DeviceRotationRate\":[],\"DeviceRotationRateInit\":[],\"DhKeyDeriveParams\":[],\"DirectionSetting\":[],\"Directory\":[],\"DirectoryPickerOptions\":[],\"DisplayMediaStreamConstraints\":[],\"DisplayNameOptions\":[],\"DisplayNameResult\":[],\"DistanceModelType\":[],\"DnsCacheDict\":[],\"DnsCacheEntry\":[],\"DnsLookupDict\":[],\"Document\":[\"EventTarget\",\"Node\"],\"DocumentFragment\":[\"EventTarget\",\"Node\"],\"DocumentTimeline\":[\"AnimationTimeline\"],\"DocumentTimelineOptions\":[],\"DocumentType\":[\"EventTarget\",\"Node\"],\"DomError\":[],\"DomException\":[],\"DomImplementation\":[],\"DomMatrix\":[\"DomMatrixReadOnly\"],\"DomMatrix2dInit\":[],\"DomMatrixInit\":[],\"DomMatrixReadOnly\":[],\"DomParser\":[],\"DomPoint\":[\"DomPointReadOnly\"],\"DomPointInit\":[],\"DomPointReadOnly\":[],\"DomQuad\":[],\"DomQuadInit\":[],\"DomQuadJson\":[],\"DomRect\":[\"DomRectReadOnly\"],\"DomRectInit\":[],\"DomRectList\":[],\"DomRectReadOnly\":[],\"DomRequest\":[\"EventTarget\"],\"DomRequestReadyState\":[],\"DomStringList\":[],\"DomStringMap\":[],\"DomTokenList\":[],\"DomWindowResizeEventDetail\":[],\"DoubleRange\":[],\"DragEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"DragEventInit\":[],\"DynamicsCompressorNode\":[\"AudioNode\",\"EventTarget\"],\"DynamicsCompressorOptions\":[],\"EcKeyAlgorithm\":[],\"EcKeyGenParams\":[],\"EcKeyImportParams\":[],\"EcdhKeyDeriveParams\":[],\"EcdsaParams\":[],\"EffectTiming\":[],\"Element\":[\"EventTarget\",\"Node\"],\"ElementCreationOptions\":[],\"ElementDefinitionOptions\":[],\"EncodedAudioChunk\":[],\"EncodedAudioChunkInit\":[],\"EncodedAudioChunkMetadata\":[],\"EncodedAudioChunkType\":[],\"EncodedVideoChunk\":[],\"EncodedVideoChunkInit\":[],\"EncodedVideoChunkMetadata\":[],\"EncodedVideoChunkType\":[],\"EndingTypes\":[],\"ErrorCallback\":[],\"ErrorEvent\":[\"Event\"],\"ErrorEventInit\":[],\"Event\":[],\"EventInit\":[],\"EventListener\":[],\"EventListenerOptions\":[],\"EventModifierInit\":[],\"EventSource\":[\"EventTarget\"],\"EventSourceInit\":[],\"EventTarget\":[],\"Exception\":[],\"ExtBlendMinmax\":[],\"ExtColorBufferFloat\":[],\"ExtColorBufferHalfFloat\":[],\"ExtDisjointTimerQuery\":[],\"ExtFragDepth\":[],\"ExtSRgb\":[],\"ExtShaderTextureLod\":[],\"ExtTextureFilterAnisotropic\":[],\"ExtTextureNorm16\":[],\"ExtendableEvent\":[\"Event\"],\"ExtendableEventInit\":[],\"ExtendableMessageEvent\":[\"Event\",\"ExtendableEvent\"],\"ExtendableMessageEventInit\":[],\"External\":[],\"FakePluginMimeEntry\":[],\"FakePluginTagInit\":[],\"FetchEvent\":[\"Event\",\"ExtendableEvent\"],\"FetchEventInit\":[],\"FetchObserver\":[\"EventTarget\"],\"FetchReadableStreamReadDataArray\":[],\"FetchReadableStreamReadDataDone\":[],\"FetchState\":[],\"File\":[\"Blob\"],\"FileCallback\":[],\"FileList\":[],\"FilePickerAcceptType\":[],\"FilePickerOptions\":[],\"FilePropertyBag\":[],\"FileReader\":[\"EventTarget\"],\"FileReaderSync\":[],\"FileSystem\":[],\"FileSystemCreateWritableOptions\":[],\"FileSystemDirectoryEntry\":[\"FileSystemEntry\"],\"FileSystemDirectoryHandle\":[\"FileSystemHandle\"],\"FileSystemDirectoryReader\":[],\"FileSystemEntriesCallback\":[],\"FileSystemEntry\":[],\"FileSystemEntryCallback\":[],\"FileSystemFileEntry\":[\"FileSystemEntry\"],\"FileSystemFileHandle\":[\"FileSystemHandle\"],\"FileSystemFlags\":[],\"FileSystemGetDirectoryOptions\":[],\"FileSystemGetFileOptions\":[],\"FileSystemHandle\":[],\"FileSystemHandleKind\":[],\"FileSystemHandlePermissionDescriptor\":[],\"FileSystemPermissionDescriptor\":[],\"FileSystemPermissionMode\":[],\"FileSystemReadWriteOptions\":[],\"FileSystemRemoveOptions\":[],\"FileSystemSyncAccessHandle\":[],\"FileSystemWritableFileStream\":[\"WritableStream\"],\"FillMode\":[],\"FlashClassification\":[],\"FlowControlType\":[],\"FocusEvent\":[\"Event\",\"UiEvent\"],\"FocusEventInit\":[],\"FocusOptions\":[],\"FontData\":[],\"FontFace\":[],\"FontFaceDescriptors\":[],\"FontFaceLoadStatus\":[],\"FontFaceSet\":[\"EventTarget\"],\"FontFaceSetIterator\":[],\"FontFaceSetIteratorResult\":[],\"FontFaceSetLoadEvent\":[\"Event\"],\"FontFaceSetLoadEventInit\":[],\"FontFaceSetLoadStatus\":[],\"FormData\":[],\"FrameType\":[],\"FuzzingFunctions\":[],\"GainNode\":[\"AudioNode\",\"EventTarget\"],\"GainOptions\":[],\"Gamepad\":[],\"GamepadButton\":[],\"GamepadEffectParameters\":[],\"GamepadEvent\":[\"Event\"],\"GamepadEventInit\":[],\"GamepadHand\":[],\"GamepadHapticActuator\":[],\"GamepadHapticActuatorType\":[],\"GamepadHapticEffectType\":[],\"GamepadHapticsResult\":[],\"GamepadMappingType\":[],\"GamepadPose\":[],\"GamepadTouch\":[],\"Geolocation\":[],\"GestureEvent\":[\"Event\",\"UiEvent\"],\"GetAnimationsOptions\":[],\"GetRootNodeOptions\":[],\"GetUserMediaRequest\":[],\"Gpu\":[],\"GpuAdapter\":[],\"GpuAdapterInfo\":[],\"GpuAddressMode\":[],\"GpuAutoLayoutMode\":[],\"GpuBindGroup\":[],\"GpuBindGroupDescriptor\":[],\"GpuBindGroupEntry\":[],\"GpuBindGroupLayout\":[],\"GpuBindGroupLayoutDescriptor\":[],\"GpuBindGroupLayoutEntry\":[],\"GpuBlendComponent\":[],\"GpuBlendFactor\":[],\"GpuBlendOperation\":[],\"GpuBlendState\":[],\"GpuBuffer\":[],\"GpuBufferBinding\":[],\"GpuBufferBindingLayout\":[],\"GpuBufferBindingType\":[],\"GpuBufferDescriptor\":[],\"GpuBufferMapState\":[],\"GpuCanvasAlphaMode\":[],\"GpuCanvasConfiguration\":[],\"GpuCanvasContext\":[],\"GpuCanvasToneMapping\":[],\"GpuCanvasToneMappingMode\":[],\"GpuColorDict\":[],\"GpuColorTargetState\":[],\"GpuCommandBuffer\":[],\"GpuCommandBufferDescriptor\":[],\"GpuCommandEncoder\":[],\"GpuCommandEncoderDescriptor\":[],\"GpuCompareFunction\":[],\"GpuCompilationInfo\":[],\"GpuCompilationMessage\":[],\"GpuCompilationMessageType\":[],\"GpuComputePassDescriptor\":[],\"GpuComputePassEncoder\":[],\"GpuComputePassTimestampWrites\":[],\"GpuComputePipeline\":[],\"GpuComputePipelineDescriptor\":[],\"GpuCopyExternalImageDestInfo\":[],\"GpuCopyExternalImageSourceInfo\":[],\"GpuCullMode\":[],\"GpuDepthStencilState\":[],\"GpuDevice\":[\"EventTarget\"],\"GpuDeviceDescriptor\":[],\"GpuDeviceLostInfo\":[],\"GpuDeviceLostReason\":[],\"GpuError\":[],\"GpuErrorFilter\":[],\"GpuExtent3dDict\":[],\"GpuExternalTexture\":[],\"GpuExternalTextureBindingLayout\":[],\"GpuExternalTextureDescriptor\":[],\"GpuFeatureName\":[],\"GpuFilterMode\":[],\"GpuFragmentState\":[],\"GpuFrontFace\":[],\"GpuIndexFormat\":[],\"GpuInternalError\":[\"GpuError\"],\"GpuLoadOp\":[],\"GpuMipmapFilterMode\":[],\"GpuMultisampleState\":[],\"GpuObjectDescriptorBase\":[],\"GpuOrigin2dDict\":[],\"GpuOrigin3dDict\":[],\"GpuOutOfMemoryError\":[\"GpuError\"],\"GpuPipelineDescriptorBase\":[],\"GpuPipelineError\":[\"DomException\"],\"GpuPipelineErrorInit\":[],\"GpuPipelineErrorReason\":[],\"GpuPipelineLayout\":[],\"GpuPipelineLayoutDescriptor\":[],\"GpuPowerPreference\":[],\"GpuPrimitiveState\":[],\"GpuPrimitiveTopology\":[],\"GpuProgrammableStage\":[],\"GpuQuerySet\":[],\"GpuQuerySetDescriptor\":[],\"GpuQueryType\":[],\"GpuQueue\":[],\"GpuQueueDescriptor\":[],\"GpuRenderBundle\":[],\"GpuRenderBundleDescriptor\":[],\"GpuRenderBundleEncoder\":[],\"GpuRenderBundleEncoderDescriptor\":[],\"GpuRenderPassColorAttachment\":[],\"GpuRenderPassDepthStencilAttachment\":[],\"GpuRenderPassDescriptor\":[],\"GpuRenderPassEncoder\":[],\"GpuRenderPassLayout\":[],\"GpuRenderPassTimestampWrites\":[],\"GpuRenderPipeline\":[],\"GpuRenderPipelineDescriptor\":[],\"GpuRequestAdapterOptions\":[],\"GpuSampler\":[],\"GpuSamplerBindingLayout\":[],\"GpuSamplerBindingType\":[],\"GpuSamplerDescriptor\":[],\"GpuShaderModule\":[],\"GpuShaderModuleCompilationHint\":[],\"GpuShaderModuleDescriptor\":[],\"GpuStencilFaceState\":[],\"GpuStencilOperation\":[],\"GpuStorageTextureAccess\":[],\"GpuStorageTextureBindingLayout\":[],\"GpuStoreOp\":[],\"GpuSupportedFeatures\":[],\"GpuSupportedLimits\":[],\"GpuTexelCopyBufferInfo\":[],\"GpuTexelCopyBufferLayout\":[],\"GpuTexelCopyTextureInfo\":[],\"GpuTexture\":[],\"GpuTextureAspect\":[],\"GpuTextureBindingLayout\":[],\"GpuTextureDescriptor\":[],\"GpuTextureDimension\":[],\"GpuTextureFormat\":[],\"GpuTextureSampleType\":[],\"GpuTextureView\":[],\"GpuTextureViewDescriptor\":[],\"GpuTextureViewDimension\":[],\"GpuUncapturedErrorEvent\":[\"Event\"],\"GpuUncapturedErrorEventInit\":[],\"GpuValidationError\":[\"GpuError\"],\"GpuVertexAttribute\":[],\"GpuVertexBufferLayout\":[],\"GpuVertexFormat\":[],\"GpuVertexState\":[],\"GpuVertexStepMode\":[],\"GroupedHistoryEventInit\":[],\"HalfOpenInfoDict\":[],\"HardwareAcceleration\":[],\"HashChangeEvent\":[\"Event\"],\"HashChangeEventInit\":[],\"Headers\":[],\"HeadersGuardEnum\":[],\"Hid\":[\"EventTarget\"],\"HidCollectionInfo\":[],\"HidConnectionEvent\":[\"Event\"],\"HidConnectionEventInit\":[],\"HidDevice\":[\"EventTarget\"],\"HidDeviceFilter\":[],\"HidDeviceRequestOptions\":[],\"HidInputReportEvent\":[\"Event\"],\"HidInputReportEventInit\":[],\"HidReportInfo\":[],\"HidReportItem\":[],\"HidUnitSystem\":[],\"HiddenPluginEventInit\":[],\"History\":[],\"HitRegionOptions\":[],\"HkdfParams\":[],\"HmacDerivedKeyParams\":[],\"HmacImportParams\":[],\"HmacKeyAlgorithm\":[],\"HmacKeyGenParams\":[],\"HtmlAllCollection\":[],\"HtmlAnchorElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAudioElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HtmlBaseElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBodyElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlButtonElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCanvasElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCollection\":[],\"HtmlDListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDetailsElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDialogElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDirectoryElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDivElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"HtmlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"HtmlEmbedElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFieldSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFontElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFormControlsCollection\":[\"HtmlCollection\"],\"HtmlFormElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadingElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHtmlElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlIFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlImageElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlInputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLabelElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLegendElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLiElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLinkElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMapElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMediaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuItemElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMetaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMeterElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlModElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlObjectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptGroupElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionsCollection\":[\"HtmlCollection\"],\"HtmlOutputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParagraphElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParamElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPictureElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPreElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlProgressElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlQuoteElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlScriptElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSelectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSlotElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSourceElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSpanElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlStyleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCaptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCellElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableColElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableRowElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableSectionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTemplateElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTextAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTimeElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTitleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTrackElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUnknownElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlVideoElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HttpConnDict\":[],\"HttpConnInfo\":[],\"HttpConnectionElement\":[],\"IdbCursor\":[],\"IdbCursorDirection\":[],\"IdbCursorWithValue\":[\"IdbCursor\"],\"IdbDatabase\":[\"EventTarget\"],\"IdbFactory\":[],\"IdbFileHandle\":[\"EventTarget\"],\"IdbFileMetadataParameters\":[],\"IdbFileRequest\":[\"DomRequest\",\"EventTarget\"],\"IdbIndex\":[],\"IdbIndexParameters\":[],\"IdbKeyRange\":[],\"IdbLocaleAwareKeyRange\":[\"IdbKeyRange\"],\"IdbMutableFile\":[\"EventTarget\"],\"IdbObjectStore\":[],\"IdbObjectStoreParameters\":[],\"IdbOpenDbOptions\":[],\"IdbOpenDbRequest\":[\"EventTarget\",\"IdbRequest\"],\"IdbRequest\":[\"EventTarget\"],\"IdbRequestReadyState\":[],\"IdbTransaction\":[\"EventTarget\"],\"IdbTransactionDurability\":[],\"IdbTransactionMode\":[],\"IdbTransactionOptions\":[],\"IdbVersionChangeEvent\":[\"Event\"],\"IdbVersionChangeEventInit\":[],\"IdleDeadline\":[],\"IdleRequestOptions\":[],\"IirFilterNode\":[\"AudioNode\",\"EventTarget\"],\"IirFilterOptions\":[],\"ImageBitmap\":[],\"ImageBitmapOptions\":[],\"ImageBitmapRenderingContext\":[],\"ImageCapture\":[],\"ImageCaptureError\":[],\"ImageCaptureErrorEvent\":[\"Event\"],\"ImageCaptureErrorEventInit\":[],\"ImageData\":[],\"ImageDecodeOptions\":[],\"ImageDecodeResult\":[],\"ImageDecoder\":[],\"ImageDecoderInit\":[],\"ImageEncodeOptions\":[],\"ImageOrientation\":[],\"ImageTrack\":[\"EventTarget\"],\"ImageTrackList\":[],\"InputDeviceInfo\":[\"MediaDeviceInfo\"],\"InputEvent\":[\"Event\",\"UiEvent\"],\"InputEventInit\":[],\"IntersectionObserver\":[],\"IntersectionObserverEntry\":[],\"IntersectionObserverEntryInit\":[],\"IntersectionObserverInit\":[],\"IntlUtils\":[],\"IsInputPendingOptions\":[],\"IterableKeyAndValueResult\":[],\"IterableKeyOrValueResult\":[],\"IterationCompositeOperation\":[],\"JsonWebKey\":[],\"KeyAlgorithm\":[],\"KeyEvent\":[],\"KeyFrameRequestEvent\":[\"Event\"],\"KeyIdsInitData\":[],\"KeyboardEvent\":[\"Event\",\"UiEvent\"],\"KeyboardEventInit\":[],\"KeyframeAnimationOptions\":[],\"KeyframeEffect\":[\"AnimationEffect\"],\"KeyframeEffectOptions\":[],\"L10nElement\":[],\"L10nValue\":[],\"LargeBlobSupport\":[],\"LatencyMode\":[],\"LifecycleCallbacks\":[],\"LineAlignSetting\":[],\"ListBoxObject\":[],\"LocalMediaStream\":[\"EventTarget\",\"MediaStream\"],\"LocaleInfo\":[],\"Location\":[],\"Lock\":[],\"LockInfo\":[],\"LockManager\":[],\"LockManagerSnapshot\":[],\"LockMode\":[],\"LockOptions\":[],\"MathMlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"MediaCapabilities\":[],\"MediaCapabilitiesInfo\":[],\"MediaConfiguration\":[],\"MediaDecodingConfiguration\":[],\"MediaDecodingType\":[],\"MediaDeviceInfo\":[],\"MediaDeviceKind\":[],\"MediaDevices\":[\"EventTarget\"],\"MediaElementAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaElementAudioSourceOptions\":[],\"MediaEncodingConfiguration\":[],\"MediaEncodingType\":[],\"MediaEncryptedEvent\":[\"Event\"],\"MediaError\":[],\"MediaImage\":[],\"MediaKeyError\":[\"Event\"],\"MediaKeyMessageEvent\":[\"Event\"],\"MediaKeyMessageEventInit\":[],\"MediaKeyMessageType\":[],\"MediaKeyNeededEventInit\":[],\"MediaKeySession\":[\"EventTarget\"],\"MediaKeySessionType\":[],\"MediaKeyStatus\":[],\"MediaKeyStatusMap\":[],\"MediaKeySystemAccess\":[],\"MediaKeySystemConfiguration\":[],\"MediaKeySystemMediaCapability\":[],\"MediaKeySystemStatus\":[],\"MediaKeys\":[],\"MediaKeysPolicy\":[],\"MediaKeysRequirement\":[],\"MediaList\":[],\"MediaMetadata\":[],\"MediaMetadataInit\":[],\"MediaPositionState\":[],\"MediaQueryList\":[\"EventTarget\"],\"MediaQueryListEvent\":[\"Event\"],\"MediaQueryListEventInit\":[],\"MediaRecorder\":[\"EventTarget\"],\"MediaRecorderErrorEvent\":[\"Event\"],\"MediaRecorderErrorEventInit\":[],\"MediaRecorderOptions\":[],\"MediaSession\":[],\"MediaSessionAction\":[],\"MediaSessionActionDetails\":[],\"MediaSessionPlaybackState\":[],\"MediaSource\":[\"EventTarget\"],\"MediaSourceEndOfStreamError\":[],\"MediaSourceEnum\":[],\"MediaSourceReadyState\":[],\"MediaStream\":[\"EventTarget\"],\"MediaStreamAudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceOptions\":[],\"MediaStreamConstraints\":[],\"MediaStreamError\":[],\"MediaStreamEvent\":[\"Event\"],\"MediaStreamEventInit\":[],\"MediaStreamTrack\":[\"EventTarget\"],\"MediaStreamTrackEvent\":[\"Event\"],\"MediaStreamTrackEventInit\":[],\"MediaStreamTrackGenerator\":[\"EventTarget\",\"MediaStreamTrack\"],\"MediaStreamTrackGeneratorInit\":[],\"MediaStreamTrackProcessor\":[],\"MediaStreamTrackProcessorInit\":[],\"MediaStreamTrackState\":[],\"MediaTrackCapabilities\":[],\"MediaTrackConstraintSet\":[],\"MediaTrackConstraints\":[],\"MediaTrackSettings\":[],\"MediaTrackSupportedConstraints\":[],\"MemoryAttribution\":[],\"MemoryAttributionContainer\":[],\"MemoryBreakdownEntry\":[],\"MemoryMeasurement\":[],\"MessageChannel\":[],\"MessageEvent\":[\"Event\"],\"MessageEventInit\":[],\"MessagePort\":[\"EventTarget\"],\"MidiAccess\":[\"EventTarget\"],\"MidiConnectionEvent\":[\"Event\"],\"MidiConnectionEventInit\":[],\"MidiInput\":[\"EventTarget\",\"MidiPort\"],\"MidiInputMap\":[],\"MidiMessageEvent\":[\"Event\"],\"MidiMessageEventInit\":[],\"MidiOptions\":[],\"MidiOutput\":[\"EventTarget\",\"MidiPort\"],\"MidiOutputMap\":[],\"MidiPort\":[\"EventTarget\"],\"MidiPortConnectionState\":[],\"MidiPortDeviceState\":[],\"MidiPortType\":[],\"MimeType\":[],\"MimeTypeArray\":[],\"MouseEvent\":[\"Event\",\"UiEvent\"],\"MouseEventInit\":[],\"MouseScrollEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"MozDebug\":[],\"MutationEvent\":[\"Event\"],\"MutationObserver\":[],\"MutationObserverInit\":[],\"MutationObservingInfo\":[],\"MutationRecord\":[],\"NamedNodeMap\":[],\"NativeOsFileReadOptions\":[],\"NativeOsFileWriteAtomicOptions\":[],\"NavigationType\":[],\"Navigator\":[],\"NavigatorAutomationInformation\":[],\"NavigatorUaBrandVersion\":[],\"NavigatorUaData\":[],\"NetworkCommandOptions\":[],\"NetworkInformation\":[\"EventTarget\"],\"NetworkResultOptions\":[],\"Node\":[\"EventTarget\"],\"NodeFilter\":[],\"NodeIterator\":[],\"NodeList\":[],\"Notification\":[\"EventTarget\"],\"NotificationAction\":[],\"NotificationDirection\":[],\"NotificationEvent\":[\"Event\",\"ExtendableEvent\"],\"NotificationEventInit\":[],\"NotificationOptions\":[],\"NotificationPermission\":[],\"ObserverCallback\":[],\"OesElementIndexUint\":[],\"OesStandardDerivatives\":[],\"OesTextureFloat\":[],\"OesTextureFloatLinear\":[],\"OesTextureHalfFloat\":[],\"OesTextureHalfFloatLinear\":[],\"OesVertexArrayObject\":[],\"OfflineAudioCompletionEvent\":[\"Event\"],\"OfflineAudioCompletionEventInit\":[],\"OfflineAudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"OfflineAudioContextOptions\":[],\"OfflineResourceList\":[\"EventTarget\"],\"OffscreenCanvas\":[\"EventTarget\"],\"OffscreenCanvasRenderingContext2d\":[],\"OpenFilePickerOptions\":[],\"OpenWindowEventDetail\":[],\"OptionalEffectTiming\":[],\"OrientationLockType\":[],\"OrientationType\":[],\"OscillatorNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"OscillatorOptions\":[],\"OscillatorType\":[],\"OverSampleType\":[],\"OvrMultiview2\":[],\"PageTransitionEvent\":[\"Event\"],\"PageTransitionEventInit\":[],\"PaintRequest\":[],\"PaintRequestList\":[],\"PaintWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"PannerNode\":[\"AudioNode\",\"EventTarget\"],\"PannerOptions\":[],\"PanningModelType\":[],\"ParityType\":[],\"Path2d\":[],\"PaymentAddress\":[],\"PaymentComplete\":[],\"PaymentMethodChangeEvent\":[\"Event\",\"PaymentRequestUpdateEvent\"],\"PaymentMethodChangeEventInit\":[],\"PaymentRequestUpdateEvent\":[\"Event\"],\"PaymentRequestUpdateEventInit\":[],\"PaymentResponse\":[],\"Pbkdf2Params\":[],\"PcImplIceConnectionState\":[],\"PcImplIceGatheringState\":[],\"PcImplSignalingState\":[],\"PcObserverStateType\":[],\"Performance\":[\"EventTarget\"],\"PerformanceEntry\":[],\"PerformanceEntryEventInit\":[],\"PerformanceEntryFilterOptions\":[],\"PerformanceMark\":[\"PerformanceEntry\"],\"PerformanceMeasure\":[\"PerformanceEntry\"],\"PerformanceNavigation\":[],\"PerformanceNavigationTiming\":[\"PerformanceEntry\",\"PerformanceResourceTiming\"],\"PerformanceObserver\":[],\"PerformanceObserverEntryList\":[],\"PerformanceObserverInit\":[],\"PerformanceResourceTiming\":[\"PerformanceEntry\"],\"PerformanceServerTiming\":[],\"PerformanceTiming\":[],\"PeriodicWave\":[],\"PeriodicWaveConstraints\":[],\"PeriodicWaveOptions\":[],\"PermissionDescriptor\":[],\"PermissionName\":[],\"PermissionState\":[],\"PermissionStatus\":[\"EventTarget\"],\"Permissions\":[],\"PictureInPictureEvent\":[\"Event\"],\"PictureInPictureEventInit\":[],\"PictureInPictureWindow\":[\"EventTarget\"],\"PlaneLayout\":[],\"PlaybackDirection\":[],\"Plugin\":[],\"PluginArray\":[],\"PluginCrashedEventInit\":[],\"PointerEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"PointerEventInit\":[],\"PopStateEvent\":[\"Event\"],\"PopStateEventInit\":[],\"PopupBlockedEvent\":[\"Event\"],\"PopupBlockedEventInit\":[],\"Position\":[],\"PositionAlignSetting\":[],\"PositionError\":[],\"PositionOptions\":[],\"PremultiplyAlpha\":[],\"Presentation\":[],\"PresentationAvailability\":[\"EventTarget\"],\"PresentationConnection\":[\"EventTarget\"],\"PresentationConnectionAvailableEvent\":[\"Event\"],\"PresentationConnectionAvailableEventInit\":[],\"PresentationConnectionBinaryType\":[],\"PresentationConnectionCloseEvent\":[\"Event\"],\"PresentationConnectionCloseEventInit\":[],\"PresentationConnectionClosedReason\":[],\"PresentationConnectionList\":[\"EventTarget\"],\"PresentationConnectionState\":[],\"PresentationReceiver\":[],\"PresentationRequest\":[\"EventTarget\"],\"PresentationStyle\":[],\"ProcessingInstruction\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"ProfileTimelineLayerRect\":[],\"ProfileTimelineMarker\":[],\"ProfileTimelineMessagePortOperationType\":[],\"ProfileTimelineStackFrame\":[],\"ProfileTimelineWorkerOperationType\":[],\"ProgressEvent\":[\"Event\"],\"ProgressEventInit\":[],\"PromiseNativeHandler\":[],\"PromiseRejectionEvent\":[\"Event\"],\"PromiseRejectionEventInit\":[],\"PublicKeyCredential\":[\"Credential\"],\"PublicKeyCredentialCreationOptions\":[],\"PublicKeyCredentialCreationOptionsJson\":[],\"PublicKeyCredentialDescriptor\":[],\"PublicKeyCredentialDescriptorJson\":[],\"PublicKeyCredentialEntity\":[],\"PublicKeyCredentialHints\":[],\"PublicKeyCredentialParameters\":[],\"PublicKeyCredentialRequestOptions\":[],\"PublicKeyCredentialRequestOptionsJson\":[],\"PublicKeyCredentialRpEntity\":[],\"PublicKeyCredentialType\":[],\"PublicKeyCredentialUserEntity\":[],\"PublicKeyCredentialUserEntityJson\":[],\"PushEncryptionKeyName\":[],\"PushEvent\":[\"Event\",\"ExtendableEvent\"],\"PushEventInit\":[],\"PushManager\":[],\"PushMessageData\":[],\"PushPermissionState\":[],\"PushSubscription\":[],\"PushSubscriptionInit\":[],\"PushSubscriptionJson\":[],\"PushSubscriptionKeys\":[],\"PushSubscriptionOptions\":[],\"PushSubscriptionOptionsInit\":[],\"QueryOptions\":[],\"QueuingStrategy\":[],\"QueuingStrategyInit\":[],\"RadioNodeList\":[\"NodeList\"],\"Range\":[],\"RcwnPerfStats\":[],\"RcwnStatus\":[],\"ReadableByteStreamController\":[],\"ReadableStream\":[],\"ReadableStreamByobReader\":[],\"ReadableStreamByobRequest\":[],\"ReadableStreamDefaultController\":[],\"ReadableStreamDefaultReader\":[],\"ReadableStreamGetReaderOptions\":[],\"ReadableStreamIteratorOptions\":[],\"ReadableStreamReadResult\":[],\"ReadableStreamReaderMode\":[],\"ReadableStreamType\":[],\"ReadableWritablePair\":[],\"RecordingState\":[],\"ReferrerPolicy\":[],\"RegisterRequest\":[],\"RegisterResponse\":[],\"RegisteredKey\":[],\"RegistrationOptions\":[],\"RegistrationResponseJson\":[],\"Request\":[],\"RequestCache\":[],\"RequestCredentials\":[],\"RequestDestination\":[],\"RequestDeviceOptions\":[],\"RequestInit\":[],\"RequestMediaKeySystemAccessNotification\":[],\"RequestMode\":[],\"RequestRedirect\":[],\"ResidentKeyRequirement\":[],\"ResizeObserver\":[],\"ResizeObserverBoxOptions\":[],\"ResizeObserverEntry\":[],\"ResizeObserverOptions\":[],\"ResizeObserverSize\":[],\"ResizeQuality\":[],\"Response\":[],\"ResponseInit\":[],\"ResponseType\":[],\"RsaHashedImportParams\":[],\"RsaOaepParams\":[],\"RsaOtherPrimesInfo\":[],\"RsaPssParams\":[],\"RtcAnswerOptions\":[],\"RtcBundlePolicy\":[],\"RtcCertificate\":[],\"RtcCertificateExpiration\":[],\"RtcCodecStats\":[],\"RtcConfiguration\":[],\"RtcDataChannel\":[\"EventTarget\"],\"RtcDataChannelEvent\":[\"Event\"],\"RtcDataChannelEventInit\":[],\"RtcDataChannelInit\":[],\"RtcDataChannelState\":[],\"RtcDataChannelType\":[],\"RtcDegradationPreference\":[],\"RtcEncodedAudioFrame\":[],\"RtcEncodedAudioFrameMetadata\":[],\"RtcEncodedAudioFrameOptions\":[],\"RtcEncodedVideoFrame\":[],\"RtcEncodedVideoFrameMetadata\":[],\"RtcEncodedVideoFrameOptions\":[],\"RtcEncodedVideoFrameType\":[],\"RtcFecParameters\":[],\"RtcIceCandidate\":[],\"RtcIceCandidateInit\":[],\"RtcIceCandidatePairStats\":[],\"RtcIceCandidateStats\":[],\"RtcIceComponentStats\":[],\"RtcIceConnectionState\":[],\"RtcIceCredentialType\":[],\"RtcIceGatheringState\":[],\"RtcIceServer\":[],\"RtcIceTransportPolicy\":[],\"RtcIdentityAssertion\":[],\"RtcIdentityAssertionResult\":[],\"RtcIdentityProvider\":[],\"RtcIdentityProviderDetails\":[],\"RtcIdentityProviderOptions\":[],\"RtcIdentityProviderRegistrar\":[],\"RtcIdentityValidationResult\":[],\"RtcInboundRtpStreamStats\":[],\"RtcMediaStreamStats\":[],\"RtcMediaStreamTrackStats\":[],\"RtcOfferAnswerOptions\":[],\"RtcOfferOptions\":[],\"RtcOutboundRtpStreamStats\":[],\"RtcPeerConnection\":[\"EventTarget\"],\"RtcPeerConnectionIceErrorEvent\":[\"Event\"],\"RtcPeerConnectionIceEvent\":[\"Event\"],\"RtcPeerConnectionIceEventInit\":[],\"RtcPeerConnectionState\":[],\"RtcPriorityType\":[],\"RtcRtcpParameters\":[],\"RtcRtpCapabilities\":[],\"RtcRtpCodecCapability\":[],\"RtcRtpCodecParameters\":[],\"RtcRtpContributingSource\":[],\"RtcRtpEncodingParameters\":[],\"RtcRtpHeaderExtensionCapability\":[],\"RtcRtpHeaderExtensionParameters\":[],\"RtcRtpParameters\":[],\"RtcRtpReceiver\":[],\"RtcRtpScriptTransform\":[],\"RtcRtpScriptTransformer\":[\"EventTarget\"],\"RtcRtpSender\":[],\"RtcRtpSourceEntry\":[],\"RtcRtpSourceEntryType\":[],\"RtcRtpSynchronizationSource\":[],\"RtcRtpTransceiver\":[],\"RtcRtpTransceiverDirection\":[],\"RtcRtpTransceiverInit\":[],\"RtcRtxParameters\":[],\"RtcSdpType\":[],\"RtcSessionDescription\":[],\"RtcSessionDescriptionInit\":[],\"RtcSignalingState\":[],\"RtcStats\":[],\"RtcStatsIceCandidatePairState\":[],\"RtcStatsIceCandidateType\":[],\"RtcStatsReport\":[],\"RtcStatsReportInternal\":[],\"RtcStatsType\":[],\"RtcTrackEvent\":[\"Event\"],\"RtcTrackEventInit\":[],\"RtcTransformEvent\":[\"Event\"],\"RtcTransportStats\":[],\"RtcdtmfSender\":[\"EventTarget\"],\"RtcdtmfToneChangeEvent\":[\"Event\"],\"RtcdtmfToneChangeEventInit\":[],\"RtcrtpContributingSourceStats\":[],\"RtcrtpStreamStats\":[],\"SFrameTransform\":[\"EventTarget\"],\"SFrameTransformErrorEvent\":[\"Event\"],\"SFrameTransformErrorEventInit\":[],\"SFrameTransformErrorEventType\":[],\"SFrameTransformOptions\":[],\"SFrameTransformRole\":[],\"SaveFilePickerOptions\":[],\"Scheduler\":[],\"SchedulerPostTaskOptions\":[],\"Scheduling\":[],\"Screen\":[\"EventTarget\"],\"ScreenColorGamut\":[],\"ScreenLuminance\":[],\"ScreenOrientation\":[\"EventTarget\"],\"ScriptProcessorNode\":[\"AudioNode\",\"EventTarget\"],\"ScrollAreaEvent\":[\"Event\",\"UiEvent\"],\"ScrollBehavior\":[],\"ScrollBoxObject\":[],\"ScrollIntoViewOptions\":[],\"ScrollLogicalPosition\":[],\"ScrollOptions\":[],\"ScrollRestoration\":[],\"ScrollSetting\":[],\"ScrollState\":[],\"ScrollToOptions\":[],\"ScrollViewChangeEventInit\":[],\"SecurityPolicyViolationEvent\":[\"Event\"],\"SecurityPolicyViolationEventDisposition\":[],\"SecurityPolicyViolationEventInit\":[],\"Selection\":[],\"SelectionMode\":[],\"Serial\":[\"EventTarget\"],\"SerialInputSignals\":[],\"SerialOptions\":[],\"SerialOutputSignals\":[],\"SerialPort\":[\"EventTarget\"],\"SerialPortFilter\":[],\"SerialPortInfo\":[],\"SerialPortRequestOptions\":[],\"ServerSocketOptions\":[],\"ServiceWorker\":[\"EventTarget\"],\"ServiceWorkerContainer\":[\"EventTarget\"],\"ServiceWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"ServiceWorkerRegistration\":[\"EventTarget\"],\"ServiceWorkerState\":[],\"ServiceWorkerUpdateViaCache\":[],\"ShadowRoot\":[\"DocumentFragment\",\"EventTarget\",\"Node\"],\"ShadowRootInit\":[],\"ShadowRootMode\":[],\"ShareData\":[],\"SharedWorker\":[\"EventTarget\"],\"SharedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"SignResponse\":[],\"SocketElement\":[],\"SocketOptions\":[],\"SocketReadyState\":[],\"SocketsDict\":[],\"SourceBuffer\":[\"EventTarget\"],\"SourceBufferAppendMode\":[],\"SourceBufferList\":[\"EventTarget\"],\"SpeechGrammar\":[],\"SpeechGrammarList\":[],\"SpeechRecognition\":[\"EventTarget\"],\"SpeechRecognitionAlternative\":[],\"SpeechRecognitionError\":[\"Event\"],\"SpeechRecognitionErrorCode\":[],\"SpeechRecognitionErrorInit\":[],\"SpeechRecognitionEvent\":[\"Event\"],\"SpeechRecognitionEventInit\":[],\"SpeechRecognitionResult\":[],\"SpeechRecognitionResultList\":[],\"SpeechSynthesis\":[\"EventTarget\"],\"SpeechSynthesisErrorCode\":[],\"SpeechSynthesisErrorEvent\":[\"Event\",\"SpeechSynthesisEvent\"],\"SpeechSynthesisErrorEventInit\":[],\"SpeechSynthesisEvent\":[\"Event\"],\"SpeechSynthesisEventInit\":[],\"SpeechSynthesisUtterance\":[\"EventTarget\"],\"SpeechSynthesisVoice\":[],\"StereoPannerNode\":[\"AudioNode\",\"EventTarget\"],\"StereoPannerOptions\":[],\"Storage\":[],\"StorageEstimate\":[],\"StorageEvent\":[\"Event\"],\"StorageEventInit\":[],\"StorageManager\":[],\"StorageType\":[],\"StreamPipeOptions\":[],\"StyleRuleChangeEventInit\":[],\"StyleSheet\":[],\"StyleSheetApplicableStateChangeEventInit\":[],\"StyleSheetChangeEventInit\":[],\"StyleSheetList\":[],\"SubmitEvent\":[\"Event\"],\"SubmitEventInit\":[],\"SubtleCrypto\":[],\"SupportedType\":[],\"SvcOutputMetadata\":[],\"SvgAngle\":[],\"SvgAnimateElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateMotionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateTransformElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimatedAngle\":[],\"SvgAnimatedBoolean\":[],\"SvgAnimatedEnumeration\":[],\"SvgAnimatedInteger\":[],\"SvgAnimatedLength\":[],\"SvgAnimatedLengthList\":[],\"SvgAnimatedNumber\":[],\"SvgAnimatedNumberList\":[],\"SvgAnimatedPreserveAspectRatio\":[],\"SvgAnimatedRect\":[],\"SvgAnimatedString\":[],\"SvgAnimatedTransformList\":[],\"SvgAnimationElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgBoundingBoxOptions\":[],\"SvgCircleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgClipPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgComponentTransferFunctionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgDefsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgDescElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgElement\":[\"Element\",\"EventTarget\",\"Node\"],\"SvgEllipseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgFilterElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgForeignObjectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGeometryElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgGraphicsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgLength\":[],\"SvgLengthList\":[],\"SvgLineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgLinearGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgMarkerElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMaskElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMatrix\":[],\"SvgMetadataElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgNumber\":[],\"SvgNumberList\":[],\"SvgPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPathSeg\":[],\"SvgPathSegArcAbs\":[\"SvgPathSeg\"],\"SvgPathSegArcRel\":[\"SvgPathSeg\"],\"SvgPathSegClosePath\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalRel\":[\"SvgPathSeg\"],\"SvgPathSegList\":[],\"SvgPathSegMovetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegMovetoRel\":[\"SvgPathSeg\"],\"SvgPatternElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgPoint\":[],\"SvgPointList\":[],\"SvgPolygonElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPolylineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPreserveAspectRatio\":[],\"SvgRadialGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgRect\":[],\"SvgRectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgScriptElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgStopElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgStringList\":[],\"SvgStyleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSwitchElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgSymbolElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTextContentElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgTextElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"SvgTextPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTextPositioningElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTitleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTransform\":[],\"SvgTransformList\":[],\"SvgUnitTypes\":[],\"SvgUseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgViewElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgZoomAndPan\":[],\"SvgaElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgfeBlendElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeColorMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeComponentTransferElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeCompositeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeConvolveMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDiffuseLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDisplacementMapElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDistantLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDropShadowElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFloodElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFuncAElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncBElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncGElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncRElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeGaussianBlurElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeNodeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMorphologyElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeOffsetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfePointLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpecularLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpotLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTileElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTurbulenceElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvggElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgmPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgsvgElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgtSpanElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"TaskController\":[\"AbortController\"],\"TaskControllerInit\":[],\"TaskPriority\":[],\"TaskPriorityChangeEvent\":[\"Event\"],\"TaskPriorityChangeEventInit\":[],\"TaskSignal\":[\"AbortSignal\",\"EventTarget\"],\"TaskSignalAnyInit\":[],\"TcpReadyState\":[],\"TcpServerSocket\":[\"EventTarget\"],\"TcpServerSocketEvent\":[\"Event\"],\"TcpServerSocketEventInit\":[],\"TcpSocket\":[\"EventTarget\"],\"TcpSocketBinaryType\":[],\"TcpSocketErrorEvent\":[\"Event\"],\"TcpSocketErrorEventInit\":[],\"TcpSocketEvent\":[\"Event\"],\"TcpSocketEventInit\":[],\"Text\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"TextDecodeOptions\":[],\"TextDecoder\":[],\"TextDecoderOptions\":[],\"TextEncoder\":[],\"TextMetrics\":[],\"TextTrack\":[\"EventTarget\"],\"TextTrackCue\":[\"EventTarget\"],\"TextTrackCueList\":[],\"TextTrackKind\":[],\"TextTrackList\":[\"EventTarget\"],\"TextTrackMode\":[],\"TimeEvent\":[\"Event\"],\"TimeRanges\":[],\"ToggleEvent\":[\"Event\"],\"ToggleEventInit\":[],\"TokenBinding\":[],\"TokenBindingStatus\":[],\"Touch\":[],\"TouchEvent\":[\"Event\",\"UiEvent\"],\"TouchEventInit\":[],\"TouchInit\":[],\"TouchList\":[],\"TrackEvent\":[\"Event\"],\"TrackEventInit\":[],\"TransformStream\":[],\"TransformStreamDefaultController\":[],\"Transformer\":[],\"TransitionEvent\":[\"Event\"],\"TransitionEventInit\":[],\"Transport\":[],\"TreeBoxObject\":[],\"TreeCellInfo\":[],\"TreeView\":[],\"TreeWalker\":[],\"U2f\":[],\"U2fClientData\":[],\"ULongRange\":[],\"UaDataValues\":[],\"UaLowEntropyJson\":[],\"UdpMessageEventInit\":[],\"UdpOptions\":[],\"UiEvent\":[\"Event\"],\"UiEventInit\":[],\"UnderlyingSink\":[],\"UnderlyingSource\":[],\"Url\":[],\"UrlSearchParams\":[],\"Usb\":[\"EventTarget\"],\"UsbAlternateInterface\":[],\"UsbConfiguration\":[],\"UsbConnectionEvent\":[\"Event\"],\"UsbConnectionEventInit\":[],\"UsbControlTransferParameters\":[],\"UsbDevice\":[],\"UsbDeviceFilter\":[],\"UsbDeviceRequestOptions\":[],\"UsbDirection\":[],\"UsbEndpoint\":[],\"UsbEndpointType\":[],\"UsbInTransferResult\":[],\"UsbInterface\":[],\"UsbIsochronousInTransferPacket\":[],\"UsbIsochronousInTransferResult\":[],\"UsbIsochronousOutTransferPacket\":[],\"UsbIsochronousOutTransferResult\":[],\"UsbOutTransferResult\":[],\"UsbPermissionDescriptor\":[],\"UsbPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"UsbPermissionStorage\":[],\"UsbRecipient\":[],\"UsbRequestType\":[],\"UsbTransferStatus\":[],\"UserActivation\":[],\"UserProximityEvent\":[\"Event\"],\"UserProximityEventInit\":[],\"UserVerificationRequirement\":[],\"ValidityState\":[],\"ValueEvent\":[\"Event\"],\"ValueEventInit\":[],\"VideoColorPrimaries\":[],\"VideoColorSpace\":[],\"VideoColorSpaceInit\":[],\"VideoConfiguration\":[],\"VideoDecoder\":[],\"VideoDecoderConfig\":[],\"VideoDecoderInit\":[],\"VideoDecoderSupport\":[],\"VideoEncoder\":[],\"VideoEncoderConfig\":[],\"VideoEncoderEncodeOptions\":[],\"VideoEncoderInit\":[],\"VideoEncoderSupport\":[],\"VideoFacingModeEnum\":[],\"VideoFrame\":[],\"VideoFrameBufferInit\":[],\"VideoFrameCopyToOptions\":[],\"VideoFrameInit\":[],\"VideoMatrixCoefficients\":[],\"VideoPixelFormat\":[],\"VideoPlaybackQuality\":[],\"VideoStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"VideoTrack\":[],\"VideoTrackList\":[\"EventTarget\"],\"VideoTransferCharacteristics\":[],\"ViewTransition\":[],\"VisibilityState\":[],\"VisualViewport\":[\"EventTarget\"],\"VoidCallback\":[],\"VrDisplay\":[\"EventTarget\"],\"VrDisplayCapabilities\":[],\"VrEye\":[],\"VrEyeParameters\":[],\"VrFieldOfView\":[],\"VrFrameData\":[],\"VrLayer\":[],\"VrMockController\":[],\"VrMockDisplay\":[],\"VrPose\":[],\"VrServiceTest\":[],\"VrStageParameters\":[],\"VrSubmitFrameResult\":[],\"VttCue\":[\"EventTarget\",\"TextTrackCue\"],\"VttRegion\":[],\"WakeLock\":[],\"WakeLockSentinel\":[\"EventTarget\"],\"WakeLockType\":[],\"WatchAdvertisementsOptions\":[],\"WaveShaperNode\":[\"AudioNode\",\"EventTarget\"],\"WaveShaperOptions\":[],\"WebGl2RenderingContext\":[],\"WebGlActiveInfo\":[],\"WebGlBuffer\":[],\"WebGlContextAttributes\":[],\"WebGlContextEvent\":[\"Event\"],\"WebGlContextEventInit\":[],\"WebGlFramebuffer\":[],\"WebGlPowerPreference\":[],\"WebGlProgram\":[],\"WebGlQuery\":[],\"WebGlRenderbuffer\":[],\"WebGlRenderingContext\":[],\"WebGlSampler\":[],\"WebGlShader\":[],\"WebGlShaderPrecisionFormat\":[],\"WebGlSync\":[],\"WebGlTexture\":[],\"WebGlTransformFeedback\":[],\"WebGlUniformLocation\":[],\"WebGlVertexArrayObject\":[],\"WebKitCssMatrix\":[\"DomMatrix\",\"DomMatrixReadOnly\"],\"WebSocket\":[\"EventTarget\"],\"WebSocketDict\":[],\"WebSocketElement\":[],\"WebTransport\":[],\"WebTransportBidirectionalStream\":[],\"WebTransportCloseInfo\":[],\"WebTransportCongestionControl\":[],\"WebTransportDatagramDuplexStream\":[],\"WebTransportDatagramStats\":[],\"WebTransportError\":[\"DomException\"],\"WebTransportErrorOptions\":[],\"WebTransportErrorSource\":[],\"WebTransportHash\":[],\"WebTransportOptions\":[],\"WebTransportReceiveStream\":[\"ReadableStream\"],\"WebTransportReceiveStreamStats\":[],\"WebTransportReliabilityMode\":[],\"WebTransportSendStream\":[\"WritableStream\"],\"WebTransportSendStreamOptions\":[],\"WebTransportSendStreamStats\":[],\"WebTransportStats\":[],\"WebglColorBufferFloat\":[],\"WebglCompressedTextureAstc\":[],\"WebglCompressedTextureAtc\":[],\"WebglCompressedTextureEtc\":[],\"WebglCompressedTextureEtc1\":[],\"WebglCompressedTexturePvrtc\":[],\"WebglCompressedTextureS3tc\":[],\"WebglCompressedTextureS3tcSrgb\":[],\"WebglDebugRendererInfo\":[],\"WebglDebugShaders\":[],\"WebglDepthTexture\":[],\"WebglDrawBuffers\":[],\"WebglLoseContext\":[],\"WebglMultiDraw\":[],\"WellKnownDirectory\":[],\"WgslLanguageFeatures\":[],\"WheelEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"WheelEventInit\":[],\"WidevineCdmManifest\":[],\"Window\":[\"EventTarget\"],\"WindowClient\":[\"Client\"],\"Worker\":[\"EventTarget\"],\"WorkerDebuggerGlobalScope\":[\"EventTarget\"],\"WorkerGlobalScope\":[\"EventTarget\"],\"WorkerLocation\":[],\"WorkerNavigator\":[],\"WorkerOptions\":[],\"WorkerType\":[],\"Worklet\":[],\"WorkletGlobalScope\":[],\"WorkletOptions\":[],\"WritableStream\":[],\"WritableStreamDefaultController\":[],\"WritableStreamDefaultWriter\":[],\"WriteCommandType\":[],\"WriteParams\":[],\"XPathExpression\":[],\"XPathNsResolver\":[],\"XPathResult\":[],\"XmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"XmlHttpRequest\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlHttpRequestEventTarget\":[\"EventTarget\"],\"XmlHttpRequestResponseType\":[],\"XmlHttpRequestUpload\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlSerializer\":[],\"XrBoundedReferenceSpace\":[\"EventTarget\",\"XrReferenceSpace\",\"XrSpace\"],\"XrEye\":[],\"XrFrame\":[],\"XrHand\":[],\"XrHandJoint\":[],\"XrHandedness\":[],\"XrInputSource\":[],\"XrInputSourceArray\":[],\"XrInputSourceEvent\":[\"Event\"],\"XrInputSourceEventInit\":[],\"XrInputSourcesChangeEvent\":[\"Event\"],\"XrInputSourcesChangeEventInit\":[],\"XrJointPose\":[\"XrPose\"],\"XrJointSpace\":[\"EventTarget\",\"XrSpace\"],\"XrLayer\":[\"EventTarget\"],\"XrPermissionDescriptor\":[],\"XrPermissionStatus\":[\"EventTarget\",\"PermissionStatus\"],\"XrPose\":[],\"XrReferenceSpace\":[\"EventTarget\",\"XrSpace\"],\"XrReferenceSpaceEvent\":[\"Event\"],\"XrReferenceSpaceEventInit\":[],\"XrReferenceSpaceType\":[],\"XrRenderState\":[],\"XrRenderStateInit\":[],\"XrRigidTransform\":[],\"XrSession\":[\"EventTarget\"],\"XrSessionEvent\":[\"Event\"],\"XrSessionEventInit\":[],\"XrSessionInit\":[],\"XrSessionMode\":[],\"XrSessionSupportedPermissionDescriptor\":[],\"XrSpace\":[\"EventTarget\"],\"XrSystem\":[\"EventTarget\"],\"XrTargetRayMode\":[],\"XrView\":[],\"XrViewerPose\":[\"XrPose\"],\"XrViewport\":[],\"XrVisibilityState\":[],\"XrWebGlLayer\":[\"EventTarget\",\"XrLayer\"],\"XrWebGlLayerInit\":[],\"XsltProcessor\":[],\"console\":[],\"css\":[],\"default\":[\"std\"],\"gpu_buffer_usage\":[],\"gpu_color_write\":[],\"gpu_map_mode\":[],\"gpu_shader_stage\":[],\"gpu_texture_usage\":[],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\"]}}", + "web-sys_0.3.102": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.102\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.125\"}],\"features\":{\"AbortController\":[],\"AbortSignal\":[\"EventTarget\"],\"AbstractRange\":[],\"AddEventListenerOptions\":[],\"AesCbcParams\":[],\"AesCtrParams\":[],\"AesDerivedKeyParams\":[],\"AesGcmParams\":[],\"AesKeyAlgorithm\":[],\"AesKeyGenParams\":[],\"Algorithm\":[],\"AlignSetting\":[],\"AllowedBluetoothDevice\":[],\"AllowedUsbDevice\":[],\"AlphaOption\":[],\"AnalyserNode\":[\"AudioNode\",\"EventTarget\"],\"AnalyserOptions\":[],\"AngleInstancedArrays\":[],\"Animation\":[\"EventTarget\"],\"AnimationEffect\":[],\"AnimationEvent\":[\"Event\"],\"AnimationEventInit\":[],\"AnimationPlayState\":[],\"AnimationPlaybackEvent\":[\"Event\"],\"AnimationPlaybackEventInit\":[],\"AnimationPropertyDetails\":[],\"AnimationPropertyValueDetails\":[],\"AnimationTimeline\":[],\"AssignedNodesOptions\":[],\"AttestationConveyancePreference\":[],\"Attr\":[\"EventTarget\",\"Node\"],\"AttributeNameValue\":[],\"AudioBuffer\":[],\"AudioBufferOptions\":[],\"AudioBufferSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"AudioBufferSourceOptions\":[],\"AudioConfiguration\":[],\"AudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"AudioContextLatencyCategory\":[],\"AudioContextOptions\":[],\"AudioContextState\":[],\"AudioData\":[],\"AudioDataCopyToOptions\":[],\"AudioDataInit\":[],\"AudioDecoder\":[\"EventTarget\"],\"AudioDecoderConfig\":[],\"AudioDecoderInit\":[],\"AudioDecoderSupport\":[],\"AudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"AudioEncoder\":[\"EventTarget\"],\"AudioEncoderConfig\":[],\"AudioEncoderInit\":[],\"AudioEncoderSupport\":[],\"AudioListener\":[],\"AudioNode\":[\"EventTarget\"],\"AudioNodeOptions\":[],\"AudioParam\":[],\"AudioParamMap\":[],\"AudioProcessingEvent\":[\"Event\"],\"AudioSampleFormat\":[],\"AudioScheduledSourceNode\":[\"AudioNode\",\"EventTarget\"],\"AudioSinkInfo\":[],\"AudioSinkOptions\":[],\"AudioSinkType\":[],\"AudioStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"AudioTrack\":[],\"AudioTrackList\":[\"EventTarget\"],\"AudioWorklet\":[\"Worklet\"],\"AudioWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"AudioWorkletNode\":[\"AudioNode\",\"EventTarget\"],\"AudioWorkletNodeOptions\":[],\"AudioWorkletProcessor\":[],\"AuthenticationExtensionsClientInputs\":[],\"AuthenticationExtensionsClientInputsJson\":[],\"AuthenticationExtensionsClientOutputs\":[],\"AuthenticationExtensionsClientOutputsJson\":[],\"AuthenticationExtensionsDevicePublicKeyInputs\":[],\"AuthenticationExtensionsDevicePublicKeyOutputs\":[],\"AuthenticationExtensionsLargeBlobInputs\":[],\"AuthenticationExtensionsLargeBlobOutputs\":[],\"AuthenticationExtensionsPrfInputs\":[],\"AuthenticationExtensionsPrfOutputs\":[],\"AuthenticationExtensionsPrfValues\":[],\"AuthenticationResponseJson\":[],\"AuthenticatorAssertionResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAssertionResponseJson\":[],\"AuthenticatorAttachment\":[],\"AuthenticatorAttestationResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAttestationResponseJson\":[],\"AuthenticatorResponse\":[],\"AuthenticatorSelectionCriteria\":[],\"AuthenticatorTransport\":[],\"AutoKeyword\":[],\"AutocompleteInfo\":[],\"BarProp\":[],\"BaseAudioContext\":[\"EventTarget\"],\"BaseComputedKeyframe\":[],\"BaseKeyframe\":[],\"BasePropertyIndexedKeyframe\":[],\"BasicCardRequest\":[],\"BasicCardResponse\":[],\"BasicCardType\":[],\"BatteryManager\":[\"EventTarget\"],\"BeforeUnloadEvent\":[\"Event\"],\"BinaryType\":[],\"BiquadFilterNode\":[\"AudioNode\",\"EventTarget\"],\"BiquadFilterOptions\":[],\"BiquadFilterType\":[],\"BitrateMode\":[],\"Blob\":[],\"BlobEvent\":[\"Event\"],\"BlobEventInit\":[],\"BlobPropertyBag\":[],\"BlockParsingOptions\":[],\"Bluetooth\":[\"EventTarget\"],\"BluetoothAdvertisingEvent\":[\"Event\"],\"BluetoothAdvertisingEventInit\":[],\"BluetoothCharacteristicProperties\":[],\"BluetoothDataFilterInit\":[],\"BluetoothDevice\":[\"EventTarget\"],\"BluetoothLeScanFilterInit\":[],\"BluetoothManufacturerDataMap\":[],\"BluetoothPermissionDescriptor\":[],\"BluetoothPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"BluetoothPermissionStorage\":[],\"BluetoothRemoteGattCharacteristic\":[\"EventTarget\"],\"BluetoothRemoteGattDescriptor\":[],\"BluetoothRemoteGattServer\":[],\"BluetoothRemoteGattService\":[\"EventTarget\"],\"BluetoothServiceDataMap\":[],\"BluetoothUuid\":[],\"BoxQuadOptions\":[],\"BroadcastChannel\":[\"EventTarget\"],\"BrowserElementDownloadOptions\":[],\"BrowserElementExecuteScriptOptions\":[],\"BrowserFeedWriter\":[],\"BrowserFindCaseSensitivity\":[],\"BrowserFindDirection\":[],\"ByteLengthQueuingStrategy\":[],\"Cache\":[],\"CacheBatchOperation\":[],\"CacheQueryOptions\":[],\"CacheStorage\":[],\"CacheStorageNamespace\":[],\"CanvasCaptureMediaStream\":[\"EventTarget\",\"MediaStream\"],\"CanvasCaptureMediaStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"CanvasGradient\":[],\"CanvasPattern\":[],\"CanvasRenderingContext2d\":[],\"CanvasWindingRule\":[],\"CaretChangedReason\":[],\"CaretPosition\":[],\"CaretStateChangedEventInit\":[],\"CdataSection\":[\"CharacterData\",\"EventTarget\",\"Node\",\"Text\"],\"ChannelCountMode\":[],\"ChannelInterpretation\":[],\"ChannelMergerNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelMergerOptions\":[],\"ChannelSplitterNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelSplitterOptions\":[],\"CharacterData\":[\"EventTarget\",\"Node\"],\"CheckerboardReason\":[],\"CheckerboardReport\":[],\"CheckerboardReportService\":[],\"ChromeFilePropertyBag\":[],\"ChromeWorker\":[\"EventTarget\",\"Worker\"],\"Client\":[],\"ClientQueryOptions\":[],\"ClientRectsAndTexts\":[],\"ClientType\":[],\"Clients\":[],\"Clipboard\":[\"EventTarget\"],\"ClipboardEvent\":[\"Event\"],\"ClipboardEventInit\":[],\"ClipboardItem\":[],\"ClipboardItemOptions\":[],\"ClipboardPermissionDescriptor\":[],\"ClipboardUnsanitizedFormats\":[],\"CloseEvent\":[\"Event\"],\"CloseEventInit\":[],\"CodecState\":[],\"CollectedClientData\":[],\"ColorSpaceConversion\":[],\"CommandEvent\":[\"Event\"],\"CommandEventInit\":[],\"Comment\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"CompositeOperation\":[],\"CompositionEvent\":[\"Event\",\"UiEvent\"],\"CompositionEventInit\":[],\"CompressionFormat\":[],\"CompressionStream\":[],\"ComputedEffectTiming\":[],\"ConnStatusDict\":[],\"ConnectionType\":[],\"ConsoleCounter\":[],\"ConsoleCounterError\":[],\"ConsoleEvent\":[],\"ConsoleInstance\":[],\"ConsoleInstanceOptions\":[],\"ConsoleLevel\":[],\"ConsoleLogLevel\":[],\"ConsoleProfileEvent\":[],\"ConsoleStackEntry\":[],\"ConsoleTimerError\":[],\"ConsoleTimerLogOrEnd\":[],\"ConsoleTimerStart\":[],\"ConstantSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"ConstantSourceOptions\":[],\"ConstrainBooleanParameters\":[],\"ConstrainDomStringParameters\":[],\"ConstrainDoubleRange\":[],\"ConstrainLongRange\":[],\"ContextAttributes2d\":[],\"ConvertCoordinateOptions\":[],\"ConvolverNode\":[\"AudioNode\",\"EventTarget\"],\"ConvolverOptions\":[],\"CookieChangeEvent\":[\"Event\"],\"CookieChangeEventInit\":[],\"CookieInit\":[],\"CookieListItem\":[],\"CookieSameSite\":[],\"CookieStore\":[\"EventTarget\"],\"CookieStoreDeleteOptions\":[],\"CookieStoreGetOptions\":[],\"CookieStoreManager\":[],\"Coordinates\":[],\"CountQueuingStrategy\":[],\"Credential\":[],\"CredentialCreationOptions\":[],\"CredentialPropertiesOutput\":[],\"CredentialRequestOptions\":[],\"CredentialsContainer\":[],\"Crypto\":[],\"CryptoKey\":[],\"CryptoKeyPair\":[],\"CssAnimation\":[\"Animation\",\"EventTarget\"],\"CssBoxType\":[],\"CssConditionRule\":[\"CssGroupingRule\",\"CssRule\"],\"CssCounterStyleRule\":[\"CssRule\"],\"CssFontFaceRule\":[\"CssRule\"],\"CssFontFeatureValuesRule\":[\"CssRule\"],\"CssGroupingRule\":[\"CssRule\"],\"CssImportRule\":[\"CssRule\"],\"CssKeyframeRule\":[\"CssRule\"],\"CssKeyframesRule\":[\"CssRule\"],\"CssMediaRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssNamespaceRule\":[\"CssRule\"],\"CssPageRule\":[\"CssRule\"],\"CssPseudoElement\":[],\"CssRule\":[],\"CssRuleList\":[],\"CssStyleDeclaration\":[],\"CssStyleRule\":[\"CssRule\"],\"CssStyleSheet\":[\"StyleSheet\"],\"CssStyleSheetParsingMode\":[],\"CssSupportsRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssTransition\":[\"Animation\",\"EventTarget\"],\"CssViewTransitionRule\":[\"CssRule\"],\"CustomElementRegistry\":[],\"CustomEvent\":[\"Event\"],\"CustomEventInit\":[],\"DataTransfer\":[],\"DataTransferItem\":[],\"DataTransferItemList\":[],\"DateTimeValue\":[],\"DecoderDoctorNotification\":[],\"DecoderDoctorNotificationType\":[],\"DecompressionStream\":[],\"DedicatedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"DelayNode\":[\"AudioNode\",\"EventTarget\"],\"DelayOptions\":[],\"DeviceAcceleration\":[],\"DeviceAccelerationInit\":[],\"DeviceLightEvent\":[\"Event\"],\"DeviceLightEventInit\":[],\"DeviceMotionEvent\":[\"Event\"],\"DeviceMotionEventInit\":[],\"DeviceOrientationEvent\":[\"Event\"],\"DeviceOrientationEventInit\":[],\"DeviceProximityEvent\":[\"Event\"],\"DeviceProximityEventInit\":[],\"DeviceRotationRate\":[],\"DeviceRotationRateInit\":[],\"DhKeyDeriveParams\":[],\"DirectionSetting\":[],\"Directory\":[],\"DirectoryPickerOptions\":[],\"DisplayMediaStreamConstraints\":[],\"DisplayNameOptions\":[],\"DisplayNameResult\":[],\"DistanceModelType\":[],\"DnsCacheDict\":[],\"DnsCacheEntry\":[],\"DnsLookupDict\":[],\"Document\":[\"EventTarget\",\"Node\"],\"DocumentFragment\":[\"EventTarget\",\"Node\"],\"DocumentTimeline\":[\"AnimationTimeline\"],\"DocumentTimelineOptions\":[],\"DocumentType\":[\"EventTarget\",\"Node\"],\"DomError\":[],\"DomException\":[],\"DomImplementation\":[],\"DomMatrix\":[\"DomMatrixReadOnly\"],\"DomMatrix2dInit\":[],\"DomMatrixInit\":[],\"DomMatrixReadOnly\":[],\"DomParser\":[],\"DomPoint\":[\"DomPointReadOnly\"],\"DomPointInit\":[],\"DomPointReadOnly\":[],\"DomQuad\":[],\"DomQuadInit\":[],\"DomQuadJson\":[],\"DomRect\":[\"DomRectReadOnly\"],\"DomRectInit\":[],\"DomRectList\":[],\"DomRectReadOnly\":[],\"DomRequest\":[\"EventTarget\"],\"DomRequestReadyState\":[],\"DomStringList\":[],\"DomStringMap\":[],\"DomTokenList\":[],\"DomWindowResizeEventDetail\":[],\"DoubleRange\":[],\"DragEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"DragEventInit\":[],\"DynamicsCompressorNode\":[\"AudioNode\",\"EventTarget\"],\"DynamicsCompressorOptions\":[],\"EcKeyAlgorithm\":[],\"EcKeyGenParams\":[],\"EcKeyImportParams\":[],\"EcdhKeyDeriveParams\":[],\"EcdsaParams\":[],\"EffectTiming\":[],\"Element\":[\"EventTarget\",\"Node\"],\"ElementCreationOptions\":[],\"ElementDefinitionOptions\":[],\"EncodedAudioChunk\":[],\"EncodedAudioChunkInit\":[],\"EncodedAudioChunkMetadata\":[],\"EncodedAudioChunkType\":[],\"EncodedVideoChunk\":[],\"EncodedVideoChunkInit\":[],\"EncodedVideoChunkMetadata\":[],\"EncodedVideoChunkType\":[],\"EndingTypes\":[],\"ErrorCallback\":[],\"ErrorEvent\":[\"Event\"],\"ErrorEventInit\":[],\"Event\":[],\"EventInit\":[],\"EventListener\":[],\"EventListenerOptions\":[],\"EventModifierInit\":[],\"EventSource\":[\"EventTarget\"],\"EventSourceInit\":[],\"EventTarget\":[],\"Exception\":[],\"ExtBlendMinmax\":[],\"ExtColorBufferFloat\":[],\"ExtColorBufferHalfFloat\":[],\"ExtDisjointTimerQuery\":[],\"ExtFragDepth\":[],\"ExtSRgb\":[],\"ExtShaderTextureLod\":[],\"ExtTextureFilterAnisotropic\":[],\"ExtTextureNorm16\":[],\"ExtendableCookieChangeEvent\":[\"Event\",\"ExtendableEvent\"],\"ExtendableCookieChangeEventInit\":[],\"ExtendableEvent\":[\"Event\"],\"ExtendableEventInit\":[],\"ExtendableMessageEvent\":[\"Event\",\"ExtendableEvent\"],\"ExtendableMessageEventInit\":[],\"External\":[],\"FakePluginMimeEntry\":[],\"FakePluginTagInit\":[],\"FetchEvent\":[\"Event\",\"ExtendableEvent\"],\"FetchEventInit\":[],\"FetchObserver\":[\"EventTarget\"],\"FetchReadableStreamReadDataArray\":[],\"FetchReadableStreamReadDataDone\":[],\"FetchState\":[],\"File\":[\"Blob\"],\"FileCallback\":[],\"FileList\":[],\"FilePickerAcceptType\":[],\"FilePickerOptions\":[],\"FilePropertyBag\":[],\"FileReader\":[\"EventTarget\"],\"FileReaderSync\":[],\"FileSystem\":[],\"FileSystemCreateWritableOptions\":[],\"FileSystemDirectoryEntry\":[\"FileSystemEntry\"],\"FileSystemDirectoryHandle\":[\"FileSystemHandle\"],\"FileSystemDirectoryReader\":[],\"FileSystemEntriesCallback\":[],\"FileSystemEntry\":[],\"FileSystemEntryCallback\":[],\"FileSystemFileEntry\":[\"FileSystemEntry\"],\"FileSystemFileHandle\":[\"FileSystemHandle\"],\"FileSystemFlags\":[],\"FileSystemGetDirectoryOptions\":[],\"FileSystemGetFileOptions\":[],\"FileSystemHandle\":[],\"FileSystemHandleKind\":[],\"FileSystemHandlePermissionDescriptor\":[],\"FileSystemPermissionDescriptor\":[],\"FileSystemPermissionMode\":[],\"FileSystemReadWriteOptions\":[],\"FileSystemRemoveOptions\":[],\"FileSystemSyncAccessHandle\":[],\"FileSystemSyncAccessHandleMode\":[],\"FileSystemSyncAccessHandleOptions\":[],\"FileSystemWritableFileStream\":[\"WritableStream\"],\"FillLightMode\":[],\"FillMode\":[],\"FlashClassification\":[],\"FlowControlType\":[],\"FocusEvent\":[\"Event\",\"UiEvent\"],\"FocusEventInit\":[],\"FocusOptions\":[],\"FontData\":[],\"FontFace\":[],\"FontFaceDescriptors\":[],\"FontFaceLoadStatus\":[],\"FontFaceSet\":[\"EventTarget\"],\"FontFaceSetIterator\":[],\"FontFaceSetIteratorResult\":[],\"FontFaceSetLoadEvent\":[\"Event\"],\"FontFaceSetLoadEventInit\":[],\"FontFaceSetLoadStatus\":[],\"FormData\":[],\"FrameType\":[],\"FuzzingFunctions\":[],\"GainNode\":[\"AudioNode\",\"EventTarget\"],\"GainOptions\":[],\"Gamepad\":[],\"GamepadButton\":[],\"GamepadEffectParameters\":[],\"GamepadEvent\":[\"Event\"],\"GamepadEventInit\":[],\"GamepadHand\":[],\"GamepadHapticActuator\":[],\"GamepadHapticActuatorType\":[],\"GamepadHapticEffectType\":[],\"GamepadHapticsResult\":[],\"GamepadMappingType\":[],\"GamepadPose\":[],\"GamepadTouch\":[],\"Geolocation\":[],\"GeolocationCoordinates\":[],\"GeolocationPosition\":[],\"GeolocationPositionError\":[],\"GestureEvent\":[\"Event\",\"UiEvent\"],\"GetAnimationsOptions\":[],\"GetRootNodeOptions\":[],\"GetUserMediaRequest\":[],\"Gpu\":[],\"GpuAdapter\":[],\"GpuAdapterInfo\":[],\"GpuAddressMode\":[],\"GpuAutoLayoutMode\":[],\"GpuBindGroup\":[],\"GpuBindGroupDescriptor\":[],\"GpuBindGroupEntry\":[],\"GpuBindGroupLayout\":[],\"GpuBindGroupLayoutDescriptor\":[],\"GpuBindGroupLayoutEntry\":[],\"GpuBlendComponent\":[],\"GpuBlendFactor\":[],\"GpuBlendOperation\":[],\"GpuBlendState\":[],\"GpuBuffer\":[],\"GpuBufferBinding\":[],\"GpuBufferBindingLayout\":[],\"GpuBufferBindingType\":[],\"GpuBufferDescriptor\":[],\"GpuBufferMapState\":[],\"GpuCanvasAlphaMode\":[],\"GpuCanvasConfiguration\":[],\"GpuCanvasContext\":[],\"GpuCanvasToneMapping\":[],\"GpuCanvasToneMappingMode\":[],\"GpuColorDict\":[],\"GpuColorTargetState\":[],\"GpuCommandBuffer\":[],\"GpuCommandBufferDescriptor\":[],\"GpuCommandEncoder\":[],\"GpuCommandEncoderDescriptor\":[],\"GpuCompareFunction\":[],\"GpuCompilationInfo\":[],\"GpuCompilationMessage\":[],\"GpuCompilationMessageType\":[],\"GpuComputePassDescriptor\":[],\"GpuComputePassEncoder\":[],\"GpuComputePassTimestampWrites\":[],\"GpuComputePipeline\":[],\"GpuComputePipelineDescriptor\":[],\"GpuCopyExternalImageDestInfo\":[],\"GpuCopyExternalImageSourceInfo\":[],\"GpuCullMode\":[],\"GpuDepthStencilState\":[],\"GpuDevice\":[\"EventTarget\"],\"GpuDeviceDescriptor\":[],\"GpuDeviceLostInfo\":[],\"GpuDeviceLostReason\":[],\"GpuError\":[],\"GpuErrorFilter\":[],\"GpuExtent3dDict\":[],\"GpuExternalTexture\":[],\"GpuExternalTextureBindingLayout\":[],\"GpuExternalTextureDescriptor\":[],\"GpuFeatureName\":[],\"GpuFilterMode\":[],\"GpuFragmentState\":[],\"GpuFrontFace\":[],\"GpuIndexFormat\":[],\"GpuInternalError\":[\"GpuError\"],\"GpuLoadOp\":[],\"GpuMipmapFilterMode\":[],\"GpuMultisampleState\":[],\"GpuObjectDescriptorBase\":[],\"GpuOrigin2dDict\":[],\"GpuOrigin3dDict\":[],\"GpuOutOfMemoryError\":[\"GpuError\"],\"GpuPipelineDescriptorBase\":[],\"GpuPipelineError\":[\"DomException\"],\"GpuPipelineErrorInit\":[],\"GpuPipelineErrorReason\":[],\"GpuPipelineLayout\":[],\"GpuPipelineLayoutDescriptor\":[],\"GpuPowerPreference\":[],\"GpuPrimitiveState\":[],\"GpuPrimitiveTopology\":[],\"GpuProgrammableStage\":[],\"GpuQuerySet\":[],\"GpuQuerySetDescriptor\":[],\"GpuQueryType\":[],\"GpuQueue\":[],\"GpuQueueDescriptor\":[],\"GpuRenderBundle\":[],\"GpuRenderBundleDescriptor\":[],\"GpuRenderBundleEncoder\":[],\"GpuRenderBundleEncoderDescriptor\":[],\"GpuRenderPassColorAttachment\":[],\"GpuRenderPassDepthStencilAttachment\":[],\"GpuRenderPassDescriptor\":[],\"GpuRenderPassEncoder\":[],\"GpuRenderPassLayout\":[],\"GpuRenderPassTimestampWrites\":[],\"GpuRenderPipeline\":[],\"GpuRenderPipelineDescriptor\":[],\"GpuRequestAdapterOptions\":[],\"GpuSampler\":[],\"GpuSamplerBindingLayout\":[],\"GpuSamplerBindingType\":[],\"GpuSamplerDescriptor\":[],\"GpuShaderModule\":[],\"GpuShaderModuleCompilationHint\":[],\"GpuShaderModuleDescriptor\":[],\"GpuStencilFaceState\":[],\"GpuStencilOperation\":[],\"GpuStorageTextureAccess\":[],\"GpuStorageTextureBindingLayout\":[],\"GpuStoreOp\":[],\"GpuSupportedFeatures\":[],\"GpuSupportedLimits\":[],\"GpuTexelCopyBufferInfo\":[],\"GpuTexelCopyBufferLayout\":[],\"GpuTexelCopyTextureInfo\":[],\"GpuTexture\":[],\"GpuTextureAspect\":[],\"GpuTextureBindingLayout\":[],\"GpuTextureDescriptor\":[],\"GpuTextureDimension\":[],\"GpuTextureFormat\":[],\"GpuTextureSampleType\":[],\"GpuTextureView\":[],\"GpuTextureViewDescriptor\":[],\"GpuTextureViewDimension\":[],\"GpuUncapturedErrorEvent\":[\"Event\"],\"GpuUncapturedErrorEventInit\":[],\"GpuValidationError\":[\"GpuError\"],\"GpuVertexAttribute\":[],\"GpuVertexBufferLayout\":[],\"GpuVertexFormat\":[],\"GpuVertexState\":[],\"GpuVertexStepMode\":[],\"GroupedHistoryEventInit\":[],\"HalfOpenInfoDict\":[],\"HardwareAcceleration\":[],\"HashChangeEvent\":[\"Event\"],\"HashChangeEventInit\":[],\"Headers\":[],\"HeadersGuardEnum\":[],\"Hid\":[\"EventTarget\"],\"HidCollectionInfo\":[],\"HidConnectionEvent\":[\"Event\"],\"HidConnectionEventInit\":[],\"HidDevice\":[\"EventTarget\"],\"HidDeviceFilter\":[],\"HidDeviceRequestOptions\":[],\"HidInputReportEvent\":[\"Event\"],\"HidInputReportEventInit\":[],\"HidReportInfo\":[],\"HidReportItem\":[],\"HidUnitSystem\":[],\"HiddenPluginEventInit\":[],\"Highlight\":[],\"HighlightHitResult\":[],\"HighlightRegistry\":[],\"HighlightType\":[],\"HighlightsFromPointOptions\":[],\"History\":[],\"HitRegionOptions\":[],\"HkdfParams\":[],\"HmacDerivedKeyParams\":[],\"HmacImportParams\":[],\"HmacKeyAlgorithm\":[],\"HmacKeyGenParams\":[],\"HtmlAllCollection\":[],\"HtmlAnchorElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAudioElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HtmlBaseElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBodyElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlButtonElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCanvasElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCollection\":[],\"HtmlDListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDetailsElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDialogElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDirectoryElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDivElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"HtmlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"HtmlEmbedElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFieldSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFontElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFormControlsCollection\":[\"HtmlCollection\"],\"HtmlFormElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadingElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHtmlElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlIFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlImageElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlInputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLabelElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLegendElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLiElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLinkElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMapElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMediaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuItemElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMetaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMeterElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlModElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlObjectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptGroupElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionsCollection\":[\"HtmlCollection\"],\"HtmlOutputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParagraphElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParamElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPictureElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPreElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlProgressElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlQuoteElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlScriptElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSelectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSlotElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSourceElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSpanElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlStyleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCaptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCellElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableColElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableRowElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableSectionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTemplateElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTextAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTimeElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTitleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTrackElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUnknownElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlVideoElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HttpConnDict\":[],\"HttpConnInfo\":[],\"HttpConnectionElement\":[],\"IdbCursor\":[],\"IdbCursorDirection\":[],\"IdbCursorWithValue\":[\"IdbCursor\"],\"IdbDatabase\":[\"EventTarget\"],\"IdbFactory\":[],\"IdbFileHandle\":[\"EventTarget\"],\"IdbFileMetadataParameters\":[],\"IdbFileRequest\":[\"DomRequest\",\"EventTarget\"],\"IdbIndex\":[],\"IdbIndexParameters\":[],\"IdbKeyRange\":[],\"IdbLocaleAwareKeyRange\":[\"IdbKeyRange\"],\"IdbMutableFile\":[\"EventTarget\"],\"IdbObjectStore\":[],\"IdbObjectStoreParameters\":[],\"IdbOpenDbOptions\":[],\"IdbOpenDbRequest\":[\"EventTarget\",\"IdbRequest\"],\"IdbRequest\":[\"EventTarget\"],\"IdbRequestReadyState\":[],\"IdbTransaction\":[\"EventTarget\"],\"IdbTransactionDurability\":[],\"IdbTransactionMode\":[],\"IdbTransactionOptions\":[],\"IdbVersionChangeEvent\":[\"Event\"],\"IdbVersionChangeEventInit\":[],\"IdleDeadline\":[],\"IdleRequestOptions\":[],\"IirFilterNode\":[\"AudioNode\",\"EventTarget\"],\"IirFilterOptions\":[],\"ImageBitmap\":[],\"ImageBitmapOptions\":[],\"ImageBitmapRenderingContext\":[],\"ImageCapture\":[],\"ImageCaptureError\":[],\"ImageCaptureErrorEvent\":[\"Event\"],\"ImageCaptureErrorEventInit\":[],\"ImageData\":[],\"ImageDecodeOptions\":[],\"ImageDecodeResult\":[],\"ImageDecoder\":[],\"ImageDecoderInit\":[],\"ImageEncodeOptions\":[],\"ImageOrientation\":[],\"ImageTrack\":[],\"ImageTrackList\":[],\"InputDeviceInfo\":[\"MediaDeviceInfo\"],\"InputEvent\":[\"Event\",\"UiEvent\"],\"InputEventInit\":[],\"IntersectionObserver\":[],\"IntersectionObserverEntry\":[],\"IntersectionObserverEntryInit\":[],\"IntersectionObserverInit\":[],\"IntlUtils\":[],\"IsInputPendingOptions\":[],\"IterableKeyAndValueResult\":[],\"IterableKeyOrValueResult\":[],\"IterationCompositeOperation\":[],\"JsonWebKey\":[],\"KeyAlgorithm\":[],\"KeyEvent\":[],\"KeyFrameRequestEvent\":[\"Event\"],\"KeyIdsInitData\":[],\"KeyboardEvent\":[\"Event\",\"UiEvent\"],\"KeyboardEventInit\":[],\"KeyframeAnimationOptions\":[],\"KeyframeEffect\":[\"AnimationEffect\"],\"KeyframeEffectOptions\":[],\"L10nElement\":[],\"L10nValue\":[],\"LargeBlobSupport\":[],\"LatencyMode\":[],\"LifecycleCallbacks\":[],\"LineAlignSetting\":[],\"ListBoxObject\":[],\"LocalMediaStream\":[\"EventTarget\",\"MediaStream\"],\"LocaleInfo\":[],\"Location\":[],\"Lock\":[],\"LockInfo\":[],\"LockManager\":[],\"LockManagerSnapshot\":[],\"LockMode\":[],\"LockOptions\":[],\"MathMlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"MediaCapabilities\":[],\"MediaCapabilitiesInfo\":[],\"MediaConfiguration\":[],\"MediaDecodingConfiguration\":[],\"MediaDecodingType\":[],\"MediaDeviceInfo\":[],\"MediaDeviceKind\":[],\"MediaDevices\":[\"EventTarget\"],\"MediaElementAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaElementAudioSourceOptions\":[],\"MediaEncodingConfiguration\":[],\"MediaEncodingType\":[],\"MediaEncryptedEvent\":[\"Event\"],\"MediaError\":[],\"MediaImage\":[],\"MediaKeyError\":[\"Event\"],\"MediaKeyMessageEvent\":[\"Event\"],\"MediaKeyMessageEventInit\":[],\"MediaKeyMessageType\":[],\"MediaKeyNeededEventInit\":[],\"MediaKeySession\":[\"EventTarget\"],\"MediaKeySessionType\":[],\"MediaKeyStatus\":[],\"MediaKeyStatusMap\":[],\"MediaKeySystemAccess\":[],\"MediaKeySystemConfiguration\":[],\"MediaKeySystemMediaCapability\":[],\"MediaKeySystemStatus\":[],\"MediaKeys\":[],\"MediaKeysPolicy\":[],\"MediaKeysRequirement\":[],\"MediaList\":[],\"MediaMetadata\":[],\"MediaMetadataInit\":[],\"MediaPositionState\":[],\"MediaQueryList\":[\"EventTarget\"],\"MediaQueryListEvent\":[\"Event\"],\"MediaQueryListEventInit\":[],\"MediaRecorder\":[\"EventTarget\"],\"MediaRecorderErrorEvent\":[\"Event\"],\"MediaRecorderErrorEventInit\":[],\"MediaRecorderOptions\":[],\"MediaSession\":[],\"MediaSessionAction\":[],\"MediaSessionActionDetails\":[],\"MediaSessionPlaybackState\":[],\"MediaSettingsRange\":[],\"MediaSource\":[\"EventTarget\"],\"MediaSourceEndOfStreamError\":[],\"MediaSourceEnum\":[],\"MediaSourceReadyState\":[],\"MediaStream\":[\"EventTarget\"],\"MediaStreamAudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceOptions\":[],\"MediaStreamConstraints\":[],\"MediaStreamError\":[],\"MediaStreamEvent\":[\"Event\"],\"MediaStreamEventInit\":[],\"MediaStreamTrack\":[\"EventTarget\"],\"MediaStreamTrackEvent\":[\"Event\"],\"MediaStreamTrackEventInit\":[],\"MediaStreamTrackGenerator\":[\"EventTarget\",\"MediaStreamTrack\"],\"MediaStreamTrackGeneratorInit\":[],\"MediaStreamTrackProcessor\":[],\"MediaStreamTrackProcessorInit\":[],\"MediaStreamTrackState\":[],\"MediaTrackCapabilities\":[],\"MediaTrackConstraintSet\":[],\"MediaTrackConstraints\":[],\"MediaTrackSettings\":[],\"MediaTrackSupportedConstraints\":[],\"MemoryAttribution\":[],\"MemoryAttributionContainer\":[],\"MemoryBreakdownEntry\":[],\"MemoryMeasurement\":[],\"MessageChannel\":[],\"MessageEvent\":[\"Event\"],\"MessageEventInit\":[],\"MessagePort\":[\"EventTarget\"],\"MeteringMode\":[],\"MidiAccess\":[\"EventTarget\"],\"MidiConnectionEvent\":[\"Event\"],\"MidiConnectionEventInit\":[],\"MidiInput\":[\"EventTarget\",\"MidiPort\"],\"MidiInputMap\":[],\"MidiMessageEvent\":[\"Event\"],\"MidiMessageEventInit\":[],\"MidiOptions\":[],\"MidiOutput\":[\"EventTarget\",\"MidiPort\"],\"MidiOutputMap\":[],\"MidiPort\":[\"EventTarget\"],\"MidiPortConnectionState\":[],\"MidiPortDeviceState\":[],\"MidiPortType\":[],\"MimeType\":[],\"MimeTypeArray\":[],\"MouseEvent\":[\"Event\",\"UiEvent\"],\"MouseEventInit\":[],\"MouseScrollEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"MozDebug\":[],\"MutationEvent\":[\"Event\"],\"MutationObserver\":[],\"MutationObserverInit\":[],\"MutationObservingInfo\":[],\"MutationRecord\":[],\"NamedNodeMap\":[],\"NativeOsFileReadOptions\":[],\"NativeOsFileWriteAtomicOptions\":[],\"NavigationType\":[],\"Navigator\":[],\"NavigatorAutomationInformation\":[],\"NavigatorUaBrandVersion\":[],\"NavigatorUaData\":[],\"NetworkCommandOptions\":[],\"NetworkInformation\":[\"EventTarget\"],\"NetworkResultOptions\":[],\"Node\":[\"EventTarget\"],\"NodeFilter\":[],\"NodeIterator\":[],\"NodeList\":[],\"Notification\":[\"EventTarget\"],\"NotificationAction\":[],\"NotificationDirection\":[],\"NotificationEvent\":[\"Event\",\"ExtendableEvent\"],\"NotificationEventInit\":[],\"NotificationOptions\":[],\"NotificationPermission\":[],\"ObserverCallback\":[],\"OesElementIndexUint\":[],\"OesStandardDerivatives\":[],\"OesTextureFloat\":[],\"OesTextureFloatLinear\":[],\"OesTextureHalfFloat\":[],\"OesTextureHalfFloatLinear\":[],\"OesVertexArrayObject\":[],\"OfflineAudioCompletionEvent\":[\"Event\"],\"OfflineAudioCompletionEventInit\":[],\"OfflineAudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"OfflineAudioContextOptions\":[],\"OfflineResourceList\":[\"EventTarget\"],\"OffscreenCanvas\":[\"EventTarget\"],\"OffscreenCanvasRenderingContext2d\":[],\"OpenFilePickerOptions\":[],\"OpenWindowEventDetail\":[],\"OptionalEffectTiming\":[],\"OrientationLockType\":[],\"OrientationType\":[],\"OscillatorNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"OscillatorOptions\":[],\"OscillatorType\":[],\"OverSampleType\":[],\"OvrMultiview2\":[],\"PageTransitionEvent\":[\"Event\"],\"PageTransitionEventInit\":[],\"PaintRequest\":[],\"PaintRequestList\":[],\"PaintWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"PannerNode\":[\"AudioNode\",\"EventTarget\"],\"PannerOptions\":[],\"PanningModelType\":[],\"ParityType\":[],\"Path2d\":[],\"PaymentAddress\":[],\"PaymentComplete\":[],\"PaymentMethodChangeEvent\":[\"Event\",\"PaymentRequestUpdateEvent\"],\"PaymentMethodChangeEventInit\":[],\"PaymentRequestUpdateEvent\":[\"Event\"],\"PaymentRequestUpdateEventInit\":[],\"PaymentResponse\":[],\"Pbkdf2Params\":[],\"PcImplIceConnectionState\":[],\"PcImplIceGatheringState\":[],\"PcImplSignalingState\":[],\"PcObserverStateType\":[],\"Performance\":[\"EventTarget\"],\"PerformanceEntry\":[],\"PerformanceEntryEventInit\":[],\"PerformanceEntryFilterOptions\":[],\"PerformanceMark\":[\"PerformanceEntry\"],\"PerformanceMarkOptions\":[],\"PerformanceMeasure\":[\"PerformanceEntry\"],\"PerformanceMeasureOptions\":[],\"PerformanceNavigation\":[],\"PerformanceNavigationTiming\":[\"PerformanceEntry\",\"PerformanceResourceTiming\"],\"PerformanceObserver\":[],\"PerformanceObserverEntryList\":[],\"PerformanceObserverInit\":[],\"PerformanceResourceTiming\":[\"PerformanceEntry\"],\"PerformanceServerTiming\":[],\"PerformanceTiming\":[],\"PeriodicWave\":[],\"PeriodicWaveConstraints\":[],\"PeriodicWaveOptions\":[],\"PermissionDescriptor\":[],\"PermissionName\":[],\"PermissionState\":[],\"PermissionStatus\":[\"EventTarget\"],\"Permissions\":[],\"PhotoCapabilities\":[],\"PhotoSettings\":[],\"PictureInPictureEvent\":[\"Event\"],\"PictureInPictureEventInit\":[],\"PictureInPictureWindow\":[\"EventTarget\"],\"PlaneLayout\":[],\"PlaybackDirection\":[],\"Plugin\":[],\"PluginArray\":[],\"PluginCrashedEventInit\":[],\"Point2d\":[],\"PointerEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"PointerEventInit\":[],\"PopStateEvent\":[\"Event\"],\"PopStateEventInit\":[],\"PopupBlockedEvent\":[\"Event\"],\"PopupBlockedEventInit\":[],\"Position\":[],\"PositionAlignSetting\":[],\"PositionError\":[],\"PositionOptions\":[],\"PremultiplyAlpha\":[],\"Presentation\":[],\"PresentationAvailability\":[\"EventTarget\"],\"PresentationConnection\":[\"EventTarget\"],\"PresentationConnectionAvailableEvent\":[\"Event\"],\"PresentationConnectionAvailableEventInit\":[],\"PresentationConnectionBinaryType\":[],\"PresentationConnectionCloseEvent\":[\"Event\"],\"PresentationConnectionCloseEventInit\":[],\"PresentationConnectionClosedReason\":[],\"PresentationConnectionList\":[\"EventTarget\"],\"PresentationConnectionState\":[],\"PresentationReceiver\":[],\"PresentationRequest\":[\"EventTarget\"],\"PresentationStyle\":[],\"ProcessingInstruction\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"ProfileTimelineLayerRect\":[],\"ProfileTimelineMarker\":[],\"ProfileTimelineMessagePortOperationType\":[],\"ProfileTimelineStackFrame\":[],\"ProfileTimelineWorkerOperationType\":[],\"ProgressEvent\":[\"Event\"],\"ProgressEventInit\":[],\"PromiseNativeHandler\":[],\"PromiseRejectionEvent\":[\"Event\"],\"PromiseRejectionEventInit\":[],\"PublicKeyCredential\":[\"Credential\"],\"PublicKeyCredentialCreationOptions\":[],\"PublicKeyCredentialCreationOptionsJson\":[],\"PublicKeyCredentialDescriptor\":[],\"PublicKeyCredentialDescriptorJson\":[],\"PublicKeyCredentialEntity\":[],\"PublicKeyCredentialHints\":[],\"PublicKeyCredentialParameters\":[],\"PublicKeyCredentialRequestOptions\":[],\"PublicKeyCredentialRequestOptionsJson\":[],\"PublicKeyCredentialRpEntity\":[],\"PublicKeyCredentialType\":[],\"PublicKeyCredentialUserEntity\":[],\"PublicKeyCredentialUserEntityJson\":[],\"PushEncryptionKeyName\":[],\"PushEvent\":[\"Event\",\"ExtendableEvent\"],\"PushEventInit\":[],\"PushManager\":[],\"PushMessageData\":[],\"PushPermissionState\":[],\"PushSubscription\":[],\"PushSubscriptionInit\":[],\"PushSubscriptionJson\":[],\"PushSubscriptionKeys\":[],\"PushSubscriptionOptions\":[],\"PushSubscriptionOptionsInit\":[],\"QueryOptions\":[],\"QueuingStrategy\":[],\"QueuingStrategyInit\":[],\"RadioNodeList\":[\"NodeList\"],\"Range\":[\"AbstractRange\"],\"RcwnPerfStats\":[],\"RcwnStatus\":[],\"ReadableByteStreamController\":[],\"ReadableStream\":[],\"ReadableStreamByobReader\":[],\"ReadableStreamByobRequest\":[],\"ReadableStreamDefaultController\":[],\"ReadableStreamDefaultReader\":[],\"ReadableStreamGetReaderOptions\":[],\"ReadableStreamIteratorOptions\":[],\"ReadableStreamReadResult\":[],\"ReadableStreamReaderMode\":[],\"ReadableStreamType\":[],\"ReadableWritablePair\":[],\"RecordingState\":[],\"RedEyeReduction\":[],\"ReferrerPolicy\":[],\"RegisterRequest\":[],\"RegisterResponse\":[],\"RegisteredKey\":[],\"RegistrationOptions\":[],\"RegistrationResponseJson\":[],\"Request\":[],\"RequestCache\":[],\"RequestCredentials\":[],\"RequestDestination\":[],\"RequestDeviceOptions\":[],\"RequestInit\":[],\"RequestMediaKeySystemAccessNotification\":[],\"RequestMode\":[],\"RequestRedirect\":[],\"ResidentKeyRequirement\":[],\"ResizeObserver\":[],\"ResizeObserverBoxOptions\":[],\"ResizeObserverEntry\":[],\"ResizeObserverOptions\":[],\"ResizeObserverSize\":[],\"ResizeQuality\":[],\"Response\":[],\"ResponseInit\":[],\"ResponseType\":[],\"RsaHashedImportParams\":[],\"RsaOaepParams\":[],\"RsaOtherPrimesInfo\":[],\"RsaPssParams\":[],\"RtcAnswerOptions\":[],\"RtcBundlePolicy\":[],\"RtcCertificate\":[],\"RtcCertificateExpiration\":[],\"RtcCodecStats\":[],\"RtcConfiguration\":[],\"RtcDataChannel\":[\"EventTarget\"],\"RtcDataChannelEvent\":[\"Event\"],\"RtcDataChannelEventInit\":[],\"RtcDataChannelInit\":[],\"RtcDataChannelState\":[],\"RtcDataChannelType\":[],\"RtcDegradationPreference\":[],\"RtcEncodedAudioFrame\":[],\"RtcEncodedAudioFrameMetadata\":[],\"RtcEncodedAudioFrameOptions\":[],\"RtcEncodedVideoFrame\":[],\"RtcEncodedVideoFrameMetadata\":[],\"RtcEncodedVideoFrameOptions\":[],\"RtcEncodedVideoFrameType\":[],\"RtcFecParameters\":[],\"RtcIceCandidate\":[],\"RtcIceCandidateInit\":[],\"RtcIceCandidatePairStats\":[],\"RtcIceCandidateStats\":[],\"RtcIceComponentStats\":[],\"RtcIceConnectionState\":[],\"RtcIceCredentialType\":[],\"RtcIceGatheringState\":[],\"RtcIceServer\":[],\"RtcIceTransportPolicy\":[],\"RtcIdentityAssertion\":[],\"RtcIdentityAssertionResult\":[],\"RtcIdentityProvider\":[],\"RtcIdentityProviderDetails\":[],\"RtcIdentityProviderOptions\":[],\"RtcIdentityProviderRegistrar\":[],\"RtcIdentityValidationResult\":[],\"RtcInboundRtpStreamStats\":[],\"RtcMediaStreamStats\":[],\"RtcMediaStreamTrackStats\":[],\"RtcOfferAnswerOptions\":[],\"RtcOfferOptions\":[],\"RtcOutboundRtpStreamStats\":[],\"RtcPeerConnection\":[\"EventTarget\"],\"RtcPeerConnectionIceErrorEvent\":[\"Event\"],\"RtcPeerConnectionIceEvent\":[\"Event\"],\"RtcPeerConnectionIceEventInit\":[],\"RtcPeerConnectionState\":[],\"RtcPriorityType\":[],\"RtcRtcpParameters\":[],\"RtcRtpCapabilities\":[],\"RtcRtpCodecCapability\":[],\"RtcRtpCodecParameters\":[],\"RtcRtpContributingSource\":[],\"RtcRtpEncodingParameters\":[],\"RtcRtpHeaderExtensionCapability\":[],\"RtcRtpHeaderExtensionParameters\":[],\"RtcRtpParameters\":[],\"RtcRtpReceiver\":[],\"RtcRtpScriptTransform\":[],\"RtcRtpScriptTransformer\":[\"EventTarget\"],\"RtcRtpSender\":[],\"RtcRtpSourceEntry\":[],\"RtcRtpSourceEntryType\":[],\"RtcRtpSynchronizationSource\":[],\"RtcRtpTransceiver\":[],\"RtcRtpTransceiverDirection\":[],\"RtcRtpTransceiverInit\":[],\"RtcRtxParameters\":[],\"RtcSdpType\":[],\"RtcSessionDescription\":[],\"RtcSessionDescriptionInit\":[],\"RtcSignalingState\":[],\"RtcStats\":[],\"RtcStatsIceCandidatePairState\":[],\"RtcStatsIceCandidateType\":[],\"RtcStatsReport\":[],\"RtcStatsReportInternal\":[],\"RtcStatsType\":[],\"RtcTrackEvent\":[\"Event\"],\"RtcTrackEventInit\":[],\"RtcTransformEvent\":[\"Event\"],\"RtcTransportStats\":[],\"RtcdtmfSender\":[\"EventTarget\"],\"RtcdtmfToneChangeEvent\":[\"Event\"],\"RtcdtmfToneChangeEventInit\":[],\"RtcrtpContributingSourceStats\":[],\"RtcrtpStreamStats\":[],\"SFrameTransform\":[\"EventTarget\"],\"SFrameTransformErrorEvent\":[\"Event\"],\"SFrameTransformErrorEventInit\":[],\"SFrameTransformErrorEventType\":[],\"SFrameTransformOptions\":[],\"SFrameTransformRole\":[],\"SaveFilePickerOptions\":[],\"Scheduler\":[],\"SchedulerPostTaskOptions\":[],\"Scheduling\":[],\"Screen\":[\"EventTarget\"],\"ScreenColorGamut\":[],\"ScreenDetailed\":[\"EventTarget\",\"Screen\"],\"ScreenDetails\":[\"EventTarget\"],\"ScreenLuminance\":[],\"ScreenOrientation\":[\"EventTarget\"],\"ScriptProcessorNode\":[\"AudioNode\",\"EventTarget\"],\"ScrollAreaEvent\":[\"Event\",\"UiEvent\"],\"ScrollBehavior\":[],\"ScrollBoxObject\":[],\"ScrollIntoViewContainer\":[],\"ScrollIntoViewOptions\":[],\"ScrollLogicalPosition\":[],\"ScrollOptions\":[],\"ScrollRestoration\":[],\"ScrollSetting\":[],\"ScrollState\":[],\"ScrollToOptions\":[],\"ScrollViewChangeEventInit\":[],\"SecurityPolicyViolationEvent\":[\"Event\"],\"SecurityPolicyViolationEventDisposition\":[],\"SecurityPolicyViolationEventInit\":[],\"Selection\":[],\"SelectionMode\":[],\"Serial\":[\"EventTarget\"],\"SerialInputSignals\":[],\"SerialOptions\":[],\"SerialOutputSignals\":[],\"SerialPort\":[\"EventTarget\"],\"SerialPortFilter\":[],\"SerialPortInfo\":[],\"SerialPortRequestOptions\":[],\"ServerSocketOptions\":[],\"ServiceWorker\":[\"EventTarget\"],\"ServiceWorkerContainer\":[\"EventTarget\"],\"ServiceWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"ServiceWorkerRegistration\":[\"EventTarget\"],\"ServiceWorkerState\":[],\"ServiceWorkerUpdateViaCache\":[],\"ShadowRoot\":[\"DocumentFragment\",\"EventTarget\",\"Node\"],\"ShadowRootInit\":[],\"ShadowRootMode\":[],\"ShareData\":[],\"SharedWorker\":[\"EventTarget\"],\"SharedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"ShowPopoverOptions\":[],\"SignResponse\":[],\"SocketElement\":[],\"SocketOptions\":[],\"SocketReadyState\":[],\"SocketsDict\":[],\"SourceBuffer\":[\"EventTarget\"],\"SourceBufferAppendMode\":[],\"SourceBufferList\":[\"EventTarget\"],\"SpeechGrammar\":[],\"SpeechGrammarList\":[],\"SpeechRecognition\":[\"EventTarget\"],\"SpeechRecognitionAlternative\":[],\"SpeechRecognitionError\":[\"Event\"],\"SpeechRecognitionErrorCode\":[],\"SpeechRecognitionErrorInit\":[],\"SpeechRecognitionEvent\":[\"Event\"],\"SpeechRecognitionEventInit\":[],\"SpeechRecognitionResult\":[],\"SpeechRecognitionResultList\":[],\"SpeechSynthesis\":[\"EventTarget\"],\"SpeechSynthesisErrorCode\":[],\"SpeechSynthesisErrorEvent\":[\"Event\",\"SpeechSynthesisEvent\"],\"SpeechSynthesisErrorEventInit\":[],\"SpeechSynthesisEvent\":[\"Event\"],\"SpeechSynthesisEventInit\":[],\"SpeechSynthesisUtterance\":[\"EventTarget\"],\"SpeechSynthesisVoice\":[],\"StartViewTransitionOptions\":[],\"StaticRange\":[\"AbstractRange\"],\"StaticRangeInit\":[],\"StereoPannerNode\":[\"AudioNode\",\"EventTarget\"],\"StereoPannerOptions\":[],\"Storage\":[],\"StorageEstimate\":[],\"StorageEvent\":[\"Event\"],\"StorageEventInit\":[],\"StorageManager\":[],\"StorageType\":[],\"StreamPipeOptions\":[],\"StyleRuleChangeEventInit\":[],\"StyleSheet\":[],\"StyleSheetApplicableStateChangeEventInit\":[],\"StyleSheetChangeEventInit\":[],\"StyleSheetList\":[],\"SubmitEvent\":[\"Event\"],\"SubmitEventInit\":[],\"SubtleCrypto\":[],\"SupportedType\":[],\"SvcOutputMetadata\":[],\"SvgAngle\":[],\"SvgAnimateElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateMotionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateTransformElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimatedAngle\":[],\"SvgAnimatedBoolean\":[],\"SvgAnimatedEnumeration\":[],\"SvgAnimatedInteger\":[],\"SvgAnimatedLength\":[],\"SvgAnimatedLengthList\":[],\"SvgAnimatedNumber\":[],\"SvgAnimatedNumberList\":[],\"SvgAnimatedPreserveAspectRatio\":[],\"SvgAnimatedRect\":[],\"SvgAnimatedString\":[],\"SvgAnimatedTransformList\":[],\"SvgAnimationElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgBoundingBoxOptions\":[],\"SvgCircleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgClipPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgComponentTransferFunctionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgDefsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgDescElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgElement\":[\"Element\",\"EventTarget\",\"Node\"],\"SvgEllipseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgFilterElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgForeignObjectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGeometryElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgGraphicsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgLength\":[],\"SvgLengthList\":[],\"SvgLineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgLinearGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgMarkerElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMaskElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMatrix\":[],\"SvgMetadataElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgNumber\":[],\"SvgNumberList\":[],\"SvgPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPathSeg\":[],\"SvgPathSegArcAbs\":[\"SvgPathSeg\"],\"SvgPathSegArcRel\":[\"SvgPathSeg\"],\"SvgPathSegClosePath\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalRel\":[\"SvgPathSeg\"],\"SvgPathSegList\":[],\"SvgPathSegMovetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegMovetoRel\":[\"SvgPathSeg\"],\"SvgPatternElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgPoint\":[],\"SvgPointList\":[],\"SvgPolygonElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPolylineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPreserveAspectRatio\":[],\"SvgRadialGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgRect\":[],\"SvgRectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgScriptElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgStopElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgStringList\":[],\"SvgStyleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSwitchElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgSymbolElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTextContentElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgTextElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"SvgTextPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTextPositioningElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTitleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTransform\":[],\"SvgTransformList\":[],\"SvgUnitTypes\":[],\"SvgUseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgViewElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgZoomAndPan\":[],\"SvgaElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgfeBlendElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeColorMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeComponentTransferElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeCompositeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeConvolveMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDiffuseLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDisplacementMapElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDistantLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDropShadowElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFloodElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFuncAElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncBElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncGElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncRElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeGaussianBlurElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeNodeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMorphologyElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeOffsetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfePointLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpecularLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpotLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTileElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTurbulenceElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvggElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgmPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgsvgElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgtSpanElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"TaskController\":[\"AbortController\"],\"TaskControllerInit\":[],\"TaskPriority\":[],\"TaskPriorityChangeEvent\":[\"Event\"],\"TaskPriorityChangeEventInit\":[],\"TaskSignal\":[\"AbortSignal\",\"EventTarget\"],\"TaskSignalAnyInit\":[],\"TcpReadyState\":[],\"TcpServerSocket\":[\"EventTarget\"],\"TcpServerSocketEvent\":[\"Event\"],\"TcpServerSocketEventInit\":[],\"TcpSocket\":[\"EventTarget\"],\"TcpSocketBinaryType\":[],\"TcpSocketErrorEvent\":[\"Event\"],\"TcpSocketErrorEventInit\":[],\"TcpSocketEvent\":[\"Event\"],\"TcpSocketEventInit\":[],\"Text\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"TextDecodeOptions\":[],\"TextDecoder\":[],\"TextDecoderOptions\":[],\"TextEncoder\":[],\"TextMetrics\":[],\"TextTrack\":[\"EventTarget\"],\"TextTrackCue\":[\"EventTarget\"],\"TextTrackCueList\":[],\"TextTrackKind\":[],\"TextTrackList\":[\"EventTarget\"],\"TextTrackMode\":[],\"TimeEvent\":[\"Event\"],\"TimeRanges\":[],\"ToggleEvent\":[\"Event\"],\"ToggleEventInit\":[],\"TogglePopoverOptions\":[],\"TokenBinding\":[],\"TokenBindingStatus\":[],\"Touch\":[],\"TouchEvent\":[\"Event\",\"UiEvent\"],\"TouchEventInit\":[],\"TouchInit\":[],\"TouchList\":[],\"TrackEvent\":[\"Event\"],\"TrackEventInit\":[],\"TransformStream\":[],\"TransformStreamDefaultController\":[],\"Transformer\":[],\"TransitionEvent\":[\"Event\"],\"TransitionEventInit\":[],\"Transport\":[],\"TreeBoxObject\":[],\"TreeCellInfo\":[],\"TreeView\":[],\"TreeWalker\":[],\"U2f\":[],\"U2fClientData\":[],\"ULongRange\":[],\"UaDataValues\":[],\"UaLowEntropyJson\":[],\"UdpMessageEventInit\":[],\"UdpOptions\":[],\"UiEvent\":[\"Event\"],\"UiEventInit\":[],\"UnderlyingSink\":[],\"UnderlyingSource\":[],\"Url\":[],\"UrlSearchParams\":[],\"Usb\":[\"EventTarget\"],\"UsbAlternateInterface\":[],\"UsbConfiguration\":[],\"UsbConnectionEvent\":[\"Event\"],\"UsbConnectionEventInit\":[],\"UsbControlTransferParameters\":[],\"UsbDevice\":[],\"UsbDeviceFilter\":[],\"UsbDeviceRequestOptions\":[],\"UsbDirection\":[],\"UsbEndpoint\":[],\"UsbEndpointType\":[],\"UsbInTransferResult\":[],\"UsbInterface\":[],\"UsbIsochronousInTransferPacket\":[],\"UsbIsochronousInTransferResult\":[],\"UsbIsochronousOutTransferPacket\":[],\"UsbIsochronousOutTransferResult\":[],\"UsbOutTransferResult\":[],\"UsbPermissionDescriptor\":[],\"UsbPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"UsbPermissionStorage\":[],\"UsbRecipient\":[],\"UsbRequestType\":[],\"UsbTransferStatus\":[],\"UserActivation\":[],\"UserProximityEvent\":[\"Event\"],\"UserProximityEventInit\":[],\"UserVerificationRequirement\":[],\"ValidityState\":[],\"ValueEvent\":[\"Event\"],\"ValueEventInit\":[],\"VideoColorPrimaries\":[],\"VideoColorSpace\":[],\"VideoColorSpaceInit\":[],\"VideoConfiguration\":[],\"VideoDecoder\":[\"EventTarget\"],\"VideoDecoderConfig\":[],\"VideoDecoderInit\":[],\"VideoDecoderSupport\":[],\"VideoEncoder\":[\"EventTarget\"],\"VideoEncoderBitrateMode\":[],\"VideoEncoderConfig\":[],\"VideoEncoderEncodeOptions\":[],\"VideoEncoderInit\":[],\"VideoEncoderSupport\":[],\"VideoFacingModeEnum\":[],\"VideoFrame\":[],\"VideoFrameBufferInit\":[],\"VideoFrameCopyToOptions\":[],\"VideoFrameInit\":[],\"VideoFrameMetadata\":[],\"VideoMatrixCoefficients\":[],\"VideoPixelFormat\":[],\"VideoPlaybackQuality\":[],\"VideoStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"VideoTrack\":[],\"VideoTrackList\":[\"EventTarget\"],\"VideoTransferCharacteristics\":[],\"ViewTransition\":[],\"ViewTransitionTypeSet\":[],\"VisibilityState\":[],\"VisualViewport\":[\"EventTarget\"],\"VoidCallback\":[],\"VrDisplay\":[\"EventTarget\"],\"VrDisplayCapabilities\":[],\"VrEye\":[],\"VrEyeParameters\":[],\"VrFieldOfView\":[],\"VrFrameData\":[],\"VrLayer\":[],\"VrMockController\":[],\"VrMockDisplay\":[],\"VrPose\":[],\"VrServiceTest\":[],\"VrStageParameters\":[],\"VrSubmitFrameResult\":[],\"VttCue\":[\"EventTarget\",\"TextTrackCue\"],\"VttRegion\":[],\"WakeLock\":[],\"WakeLockSentinel\":[\"EventTarget\"],\"WakeLockType\":[],\"WatchAdvertisementsOptions\":[],\"WaveShaperNode\":[\"AudioNode\",\"EventTarget\"],\"WaveShaperOptions\":[],\"WebGl2RenderingContext\":[],\"WebGlActiveInfo\":[],\"WebGlBuffer\":[],\"WebGlContextAttributes\":[],\"WebGlContextEvent\":[\"Event\"],\"WebGlContextEventInit\":[],\"WebGlFramebuffer\":[],\"WebGlPowerPreference\":[],\"WebGlProgram\":[],\"WebGlQuery\":[],\"WebGlRenderbuffer\":[],\"WebGlRenderingContext\":[],\"WebGlSampler\":[],\"WebGlShader\":[],\"WebGlShaderPrecisionFormat\":[],\"WebGlSync\":[],\"WebGlTexture\":[],\"WebGlTransformFeedback\":[],\"WebGlUniformLocation\":[],\"WebGlVertexArrayObject\":[],\"WebKitCssMatrix\":[\"DomMatrix\",\"DomMatrixReadOnly\"],\"WebSocket\":[\"EventTarget\"],\"WebSocketDict\":[],\"WebSocketElement\":[],\"WebTransport\":[],\"WebTransportBidirectionalStream\":[],\"WebTransportCloseInfo\":[],\"WebTransportCongestionControl\":[],\"WebTransportDatagramDuplexStream\":[],\"WebTransportDatagramStats\":[],\"WebTransportError\":[\"DomException\"],\"WebTransportErrorOptions\":[],\"WebTransportErrorSource\":[],\"WebTransportHash\":[],\"WebTransportOptions\":[],\"WebTransportReceiveStream\":[\"ReadableStream\"],\"WebTransportReceiveStreamStats\":[],\"WebTransportReliabilityMode\":[],\"WebTransportSendStream\":[\"WritableStream\"],\"WebTransportSendStreamOptions\":[],\"WebTransportSendStreamStats\":[],\"WebTransportStats\":[],\"WebglColorBufferFloat\":[],\"WebglCompressedTextureAstc\":[],\"WebglCompressedTextureAtc\":[],\"WebglCompressedTextureEtc\":[],\"WebglCompressedTextureEtc1\":[],\"WebglCompressedTexturePvrtc\":[],\"WebglCompressedTextureS3tc\":[],\"WebglCompressedTextureS3tcSrgb\":[],\"WebglDebugRendererInfo\":[],\"WebglDebugShaders\":[],\"WebglDepthTexture\":[],\"WebglDrawBuffers\":[],\"WebglLoseContext\":[],\"WebglMultiDraw\":[],\"WellKnownDirectory\":[],\"WgslLanguageFeatures\":[],\"WheelEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"WheelEventInit\":[],\"WidevineCdmManifest\":[],\"Window\":[\"EventTarget\"],\"WindowClient\":[\"Client\"],\"Worker\":[\"EventTarget\"],\"WorkerDebuggerGlobalScope\":[\"EventTarget\"],\"WorkerGlobalScope\":[\"EventTarget\"],\"WorkerLocation\":[],\"WorkerNavigator\":[],\"WorkerOptions\":[],\"WorkerType\":[],\"Worklet\":[],\"WorkletGlobalScope\":[],\"WorkletOptions\":[],\"WritableStream\":[],\"WritableStreamDefaultController\":[],\"WritableStreamDefaultWriter\":[],\"WriteCommandType\":[],\"WriteParams\":[],\"XPathExpression\":[],\"XPathNsResolver\":[],\"XPathResult\":[],\"XmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"XmlHttpRequest\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlHttpRequestEventTarget\":[\"EventTarget\"],\"XmlHttpRequestResponseType\":[],\"XmlHttpRequestUpload\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlSerializer\":[],\"XrBoundedReferenceSpace\":[\"EventTarget\",\"XrReferenceSpace\",\"XrSpace\"],\"XrEye\":[],\"XrFrame\":[],\"XrHand\":[],\"XrHandJoint\":[],\"XrHandedness\":[],\"XrInputSource\":[],\"XrInputSourceArray\":[],\"XrInputSourceEvent\":[\"Event\"],\"XrInputSourceEventInit\":[],\"XrInputSourcesChangeEvent\":[\"Event\"],\"XrInputSourcesChangeEventInit\":[],\"XrJointPose\":[\"XrPose\"],\"XrJointSpace\":[\"EventTarget\",\"XrSpace\"],\"XrLayer\":[\"EventTarget\"],\"XrPermissionDescriptor\":[],\"XrPermissionStatus\":[\"EventTarget\",\"PermissionStatus\"],\"XrPose\":[],\"XrReferenceSpace\":[\"EventTarget\",\"XrSpace\"],\"XrReferenceSpaceEvent\":[\"Event\"],\"XrReferenceSpaceEventInit\":[],\"XrReferenceSpaceType\":[],\"XrRenderState\":[],\"XrRenderStateInit\":[],\"XrRigidTransform\":[],\"XrSession\":[\"EventTarget\"],\"XrSessionEvent\":[\"Event\"],\"XrSessionEventInit\":[],\"XrSessionInit\":[],\"XrSessionMode\":[],\"XrSessionSupportedPermissionDescriptor\":[],\"XrSpace\":[\"EventTarget\"],\"XrSystem\":[\"EventTarget\"],\"XrTargetRayMode\":[],\"XrView\":[],\"XrViewerPose\":[\"XrPose\"],\"XrViewport\":[],\"XrVisibilityState\":[],\"XrWebGlLayer\":[\"EventTarget\",\"XrLayer\"],\"XrWebGlLayerInit\":[],\"XsltProcessor\":[],\"console\":[],\"css\":[],\"default\":[\"std\"],\"gpu_buffer_usage\":[],\"gpu_color_write\":[],\"gpu_map_mode\":[],\"gpu_shader_stage\":[],\"gpu_texture_usage\":[],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\"]}}", "web-time_1.1.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_feature = \\\"atomics\\\"))\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"name\":\"js-sys\",\"req\":\"^0.3.20\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"macro\"],\"kind\":\"dev\",\"name\":\"pollster\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"^0.2.70\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"features\":[\"WorkerGlobalScope\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_feature = \\\"atomics\\\"))\"},{\"features\":[\"CssStyleDeclaration\",\"Document\",\"Element\",\"HtmlTableElement\",\"HtmlTableRowElement\",\"Performance\",\"Window\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"serde\":[\"dep:serde\"]}}", "webpki-root-certs_1.0.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"}],\"features\":{}}", "webpki-roots_0.26.11": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"parent\",\"package\":\"webpki-roots\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.102\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}", diff --git a/nativelink-config/src/stores.rs b/nativelink-config/src/stores.rs index e67db1bc6..cfa69715b 100644 --- a/nativelink-config/src/stores.rs +++ b/nativelink-config/src/stores.rs @@ -1191,7 +1191,9 @@ pub struct ExperimentalGcsSpec { #[serde(deny_unknown_fields)] #[cfg_attr(feature = "dev-schema", derive(JsonSchema))] pub struct ExperimentalAzureSpec { - /// The Azure Storage account name. + /// The Azure Storage account name. Used to build the default container URL + /// `https://{account_name}.blob.core.windows.net/{container}` when `sas_url` + /// is not provided. #[serde(default, deserialize_with = "convert_string_with_shellexpand")] pub account_name: String, @@ -1199,19 +1201,24 @@ pub struct ExperimentalAzureSpec { #[serde(default, deserialize_with = "convert_string_with_shellexpand")] pub container: String, + /// Optional blob endpoint host override (for example an Azurite emulator host + /// such as `http://127.0.0.1:10000/devstoreaccount1`). When set, this replaces + /// the default `https://{account_name}.blob.core.windows.net` endpoint. The + /// container is always appended to form the final container URL. Ignored when + /// `sas_url` is set. + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub endpoint: Option, + + /// Optional pre-formed SAS URL pointing at the container. When set, the store + /// uses it directly as the container URL with no credential (the SAS token is + /// expected to already be present in the URL), and `account_name`, `container`, + /// and `endpoint` are ignored for URL construction. + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub sas_url: Option, + /// Common retry and upload configuration. #[serde(flatten)] pub common: CommonObjectSpec, - - /// Connection timeout in milliseconds. - /// Default: 3000 - #[serde(default, deserialize_with = "convert_duration_with_shellexpand")] - pub connection_timeout_s: u64, - - /// Read timeout in milliseconds. - /// Default: 3000 - #[serde(default, deserialize_with = "convert_duration_with_shellexpand")] - pub read_timeout_s: u64, } #[derive(Serialize, Deserialize, Debug, Default, Clone)] diff --git a/nativelink-error/Cargo.toml b/nativelink-error/Cargo.toml index c0493106c..2fde8ec45 100644 --- a/nativelink-error/Cargo.toml +++ b/nativelink-error/Cargo.toml @@ -24,7 +24,7 @@ prost-types = { version = "0.14.4", default-features = false, features = [ "std", ] } redis = { version = "1.0.0", default-features = false } -reqwest = { version = "0.12", default-features = false } +reqwest = { version = "0.13", default-features = false } rustls-pki-types = { version = "1.13.1", default-features = false } serde = { version = "1.0.219", default-features = false, features = ["derive"] } serde_json5 = { version = "0.2.1", default-features = false } diff --git a/nativelink-store/BUILD.bazel b/nativelink-store/BUILD.bazel index 225ca38cd..5a34f7175 100644 --- a/nativelink-store/BUILD.bazel +++ b/nativelink-store/BUILD.bazel @@ -67,8 +67,8 @@ rust_library( "@crates//:aws-smithy-runtime-api", "@crates//:aws-smithy-types", "@crates//:azure_core", - "@crates//:azure_storage", - "@crates//:azure_storage_blobs", + "@crates//:azure_identity", + "@crates//:azure_storage_blob", "@crates//:base64", "@crates//:blake3", "@crates//:byteorder", @@ -109,6 +109,7 @@ rust_library( "@crates//:tracing", "@crates//:url", "@crates//:uuid", + "@crates//:webpki-roots", "@crates//:wincode", ], ) @@ -166,8 +167,7 @@ rust_test_suite( "@crates//:aws-smithy-runtime-api", "@crates//:aws-smithy-types", "@crates//:azure_core", - "@crates//:azure_storage", - "@crates//:azure_storage_blobs", + "@crates//:azure_storage_blob", "@crates//:bytes", "@crates//:dirs", "@crates//:flate2", @@ -189,6 +189,7 @@ rust_test_suite( "@crates//:regex", "@crates//:reqwest", "@crates//:rlimit", + "@crates//:rustls", "@crates//:serde_json", "@crates//:serial_test", "@crates//:sha2", diff --git a/nativelink-store/Cargo.toml b/nativelink-store/Cargo.toml index 4d62f6fca..586bc6701 100644 --- a/nativelink-store/Cargo.toml +++ b/nativelink-store/Cargo.toml @@ -28,14 +28,15 @@ aws-smithy-runtime-api = { version = "1.7.4", default-features = false, features aws-smithy-types = { version = "1.3.0", default-features = false, features = [ "http-body-1-x", ] } -azure_core = { version = "0.21.0", default-features = false, features = [ - "hmac_rust", +azure_core = { version = "1", default-features = false, features = [ + "reqwest", + "tokio", ] } -azure_storage = { version = "0.21.0", default-features = false, features = [ - "hmac_rust", +azure_identity = { version = "1", default-features = false, features = [ + "tokio", ] } -azure_storage_blobs = { version = "0.21.0", default-features = false, features = [ - "hmac_rust", +azure_storage_blob = { version = "1", default-features = false, features = [ + "tokio", ] } base64 = { version = "0.22.1", default-features = false, features = ["std"] } blake3 = { version = "1.8.0", default-features = false } @@ -43,12 +44,15 @@ byteorder = { version = "1.5.0", default-features = false } bytes = { version = "1.10.1", default-features = false } const_format = { version = "0.2.34", default-features = false } futures = { version = "0.3.31", default-features = false, features = ["std"] } -gcloud-auth = { version = "=1.2.0", default-features = false, features = [ +gcloud-auth = { version = "1.3", default-features = false, features = [ "jwt-rust-crypto", ] } -gcloud-storage = { version = "1", default-features = false, features = [ +gcloud-storage = { version = "1.3", default-features = false, features = [ "auth", - "rustls-tls", + # reqwest 0.13's `rustls` feature pulls aws-lc-rs; `rustls-no-provider` uses + # the process-default rustls provider (ring, installed by common_s3_utils) so + # the whole binary stays on a single crypto provider. + "rustls-no-provider", ] } hex = { version = "0.4.3", default-features = false } http = { version = "1.3.1", default-features = false } @@ -89,8 +93,13 @@ redis = { version = "1.0.0", default-features = false, features = [ "tokio-comp", ] } regex = { version = "1.11.1", default-features = false } -reqwest = { version = "0.12", default-features = false } -reqwest-middleware = { version = "0.4.2", default-features = false } +reqwest = { version = "0.13", default-features = false, features = [ + "charset", + "http2", + "rustls-no-provider", + "stream", +] } +reqwest-middleware = { version = "0.5", default-features = false } rustls = { version = "0.23.27", default-features = false, features = ["ring"] } rustls-pki-types = { version = "1.13.1", default-features = false } serde = { version = "1.0.219", default-features = false } @@ -116,6 +125,7 @@ uuid = { version = "1.16.0", default-features = false, features = [ "serde", "v4", ] } +webpki-roots = { version = "1", default-features = false } wincode = { version = "0.5.4", default-features = false, features = [ "alloc", "derive", diff --git a/nativelink-store/src/azure_blob_store.rs b/nativelink-store/src/azure_blob_store.rs index 689a5e0f5..194336c81 100644 --- a/nativelink-store/src/azure_blob_store.rs +++ b/nativelink-store/src/azure_blob_store.rs @@ -19,22 +19,18 @@ use std::borrow::Cow; use std::sync::Arc; use async_trait::async_trait; -use azure_core::auth::Secret; -use azure_core::prelude::Range; -use azure_core::{Body, HttpClient, StatusCode, TransportOptions}; -use azure_storage::StorageCredentials; -use azure_storage_blobs::prelude::*; -use bytes::Bytes; +use azure_core::credentials::TokenCredential; +use azure_core::error::ErrorKind; +use azure_core::http::{RequestContent, RetryOptions, StatusCode, Transport, Url}; +use azure_identity::WorkloadIdentityCredential; +use azure_storage_blob::clients::{BlobContainerClient, BlobContainerClientOptions}; +use azure_storage_blob::models::{ + BlobClientDownloadOptions, BlobClientGetPropertiesResultHeaders, BlockLookupList, HttpRange, + StorageErrorCode, +}; use futures::future::FusedFuture; use futures::stream::{FuturesUnordered, unfold}; use futures::{FutureExt, StreamExt, TryStreamExt}; -use http::Method; -use http_body_util::Full; -use hyper::Uri; -use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; -use hyper_util::client::legacy::Client as LegacyClient; -use hyper_util::client::legacy::connect::HttpConnector as LegacyHttpConnector; -use hyper_util::rt::TokioExecutor; use nativelink_config::stores::ExperimentalAzureSpec; use nativelink_error::{Code, Error, ResultExt, make_err}; use nativelink_metric::MetricsComponent; @@ -73,347 +69,12 @@ const DEFAULT_MAX_RETRY_BUFFER_PER_REQUEST: usize = 5 * 1024 * 1024; // 5 MiB // Default maximum number of concurrent uploads const DEFAULT_MAX_CONCURRENT_UPLOADS: usize = 10; -// Maximum number of idle connections per host -const MAX_IDLE_PER_HOST: usize = 32; - -// Default connection timeout in milliseconds -const DEFAULT_CONNECTION_TIMEOUT_MS: u64 = 3000; - -// Environment variable name for Azure account key -const ACCOUNT_KEY_ENV_VAR: &str = "AZURE_STORAGE_KEY"; - -pub(crate) enum BufferedBodyState { - Buffered(Bytes), - Empty, -} - -pub(crate) struct RequestComponents { - method: Method, - uri: Uri, - version: http::Version, - headers: http::HeaderMap, - body_data: BufferedBodyState, -} - -mod body_processing { - use azure_core::Error; - - use super::{Body, BufferedBodyState}; - - #[inline] - pub(crate) async fn buffer_body(body: Body) -> Result { - match body { - Body::Bytes(bytes) if bytes.is_empty() => Ok(BufferedBodyState::Empty), - Body::Bytes(bytes) => Ok(BufferedBodyState::Buffered(bytes)), - Body::SeekableStream(_) => Err(Error::new( - azure_core::error::ErrorKind::Other, - "Unsupported body type: SeekableStream", - )), - } - } -} - -struct RequestBuilder<'a> { - components: &'a RequestComponents, -} - -impl<'a> RequestBuilder<'a> { - #[inline] - const fn new(components: &'a RequestComponents) -> Self { - Self { components } - } - - #[inline] - fn build(&self) -> Result>, http::Error> { - let mut req_builder = hyper::Request::builder() - .method(self.components.method.clone()) - .uri(self.components.uri.clone()) - .version(self.components.version); - - let headers_map = req_builder.headers_mut().unwrap(); - for (name, value) in &self.components.headers { - headers_map.insert(name, value.clone()); - } - - match &self.components.body_data { - BufferedBodyState::Buffered(bytes) => req_builder.body(Full::new(bytes.clone())), - BufferedBodyState::Empty => req_builder.body(Full::new(Bytes::new())), - } - } -} - -mod conversions { - use std::collections::HashMap; - - use azure_core::{Error, Request, Response, StatusCode, headers as azure_headers}; - use http_body_util::BodyExt; - use hyper::body::Incoming; - - use super::{BufferedBodyState, Method, RequestComponents, Uri, body_processing}; - - pub(crate) trait RequestExt { - async fn into_components(self) -> Result; - } - - impl RequestExt for Request { - async fn into_components(self) -> Result { - let method = Method::from_bytes(self.method().as_ref().as_bytes()).map_err(|e| { - Error::new( - azure_core::error::ErrorKind::Other, - format!("Failed to convert method: {e}"), - ) - })?; - - let uri = Uri::try_from(self.url().as_str()).map_err(|e| { - Error::new( - azure_core::error::ErrorKind::Other, - format!("Failed to parse URI: {e}"), - ) - })?; - - let version = http::Version::HTTP_11; // Default to HTTP/1.1 - - let mut headers = http::HeaderMap::new(); - for (name, value) in self.headers().iter() { - let header_name = - http::HeaderName::from_bytes(name.as_str().as_bytes()).map_err(|e| { - Error::new( - azure_core::error::ErrorKind::Other, - format!("Failed to convert header name: {e}"), - ) - })?; - let header_value = http::HeaderValue::from_str(value.as_str()).map_err(|e| { - Error::new( - azure_core::error::ErrorKind::Other, - format!("Failed to convert header value: {e}"), - ) - })?; - headers.insert(header_name, header_value); - } - - let body = self.body().clone(); - - let needs_buffering = matches!(method, Method::POST | Method::PUT); - - let body_data = if needs_buffering { - body_processing::buffer_body(body).await? - } else { - BufferedBodyState::Empty - }; - - Ok(RequestComponents { - method, - uri, - version, - headers, - body_data, - }) - } - } - - pub(crate) trait ResponseExt { - async fn into_azure_response(self) -> Result; - } - - impl ResponseExt for hyper::Response { - async fn into_azure_response(self) -> Result { - let (parts, body) = self.into_parts(); - - // Convert headers - let headers: HashMap<_, _> = parts - .headers - .iter() - .filter_map(|(k, v)| { - Some(( - azure_headers::HeaderName::from(k.as_str().to_owned()), - azure_headers::HeaderValue::from(v.to_str().ok()?.to_owned()), - )) - }) - .collect(); - - let data = body - .collect() - .await - .map_err(|e| { - Error::new( - azure_core::error::ErrorKind::Other, - format!("Failed to collect body: {e}"), - ) - })? - .to_bytes(); - - Ok(Response::new( - StatusCode::try_from(parts.status.as_u16()).expect("Invalid status code"), - azure_headers::Headers::from(headers), - Box::pin(futures::stream::once(futures::future::ready(Ok(data)))), - )) - } - } -} - -mod execution { - use azure_core::Response; - use bytes::Bytes; - use http_body_util::Full; - - use super::conversions::ResponseExt; - use super::{ - Code, HttpsConnector, LegacyClient, LegacyHttpConnector, RequestBuilder, RequestComponents, - RetryResult, fs, make_err, - }; - - pub(crate) async fn execute_request( - client: LegacyClient, Full>, - components: &RequestComponents, - ) -> RetryResult { - let _permit = match fs::get_permit().await { - Ok(permit) => permit, - Err(e) => { - return RetryResult::Retry(make_err!( - Code::Unavailable, - "Failed to acquire permit: {e}" - )); - } - }; - - let request = match RequestBuilder::new(components).build() { - Ok(req) => req, - Err(e) => { - return RetryResult::Err(make_err!( - Code::Internal, - "Failed to create request: {e}", - )); - } - }; - - match client.request(request).await { - Ok(resp) => match resp.into_azure_response().await { - Ok(response) => RetryResult::Ok(response), - Err(e) => RetryResult::Retry(make_err!( - Code::Unavailable, - "Failed to convert response: {e}" - )), - }, - Err(e) => RetryResult::Retry(make_err!( - Code::Unavailable, - "Failed request in AzureBlobStore: {e}" - )), - } - } - - #[inline] - pub(crate) fn create_retry_stream( - client: LegacyClient, Full>, - components: RequestComponents, - ) -> impl futures::Stream> { - futures::stream::unfold(components, move |components| { - let client_clone = client.clone(); - async move { - let result = execute_request(client_clone, &components).await; - Some((result, components)) - } - }) - } -} - -#[derive(Clone)] -pub struct AzureClient { - client: LegacyClient, Full>, - config: Arc, - retrier: Retrier, -} - -impl AzureClient { - pub fn new( - config: ExperimentalAzureSpec, - jitter_fn: Arc Duration + Send + Sync>, - ) -> Result { - let connector = Self::build_connector(&config); - let connection_timeout = if config.connection_timeout_s > 0 { - Duration::from_millis(config.connection_timeout_s) - } else { - Duration::from_millis(DEFAULT_CONNECTION_TIMEOUT_MS) - }; - let client = Self::build_client(connector, connection_timeout); - - Ok(Self { - client, - retrier: Retrier::new( - Arc::new(|duration| Box::pin(sleep(duration))), - jitter_fn, - config.common.retry.clone(), - ), - config: Arc::new(config), - }) - } - - fn build_connector(config: &ExperimentalAzureSpec) -> HttpsConnector { - install_default_rustls_crypto_provider(); - - let builder = HttpsConnectorBuilder::new().with_webpki_roots(); - - let builder_with_schemes = if config.common.insecure_allow_http { - builder.https_or_http() - } else { - builder.https_only() - }; - - if config.common.disable_http2 { - builder_with_schemes.enable_http1().build() - } else { - builder_with_schemes.enable_http1().enable_http2().build() - } - } - - fn build_client( - connector: HttpsConnector, - connection_timeout: Duration, - ) -> LegacyClient, Full> { - LegacyClient::builder(TokioExecutor::new()) - .pool_idle_timeout(connection_timeout) - .pool_max_idle_per_host(MAX_IDLE_PER_HOST) - .build(connector) - } -} +// Default public Azure Blob Storage endpoint suffix. +const DEFAULT_BLOB_ENDPOINT_SUFFIX: &str = "blob.core.windows.net"; -impl core::fmt::Debug for AzureClient { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("AzureClient") - .field("config", &self.config) - .finish() - } -} - -#[async_trait::async_trait] -impl HttpClient for AzureClient { - async fn execute_request( - &self, - request: &azure_core::Request, - ) -> azure_core::Result { - use conversions::RequestExt; - - let components = request.clone().into_components().await?; - - match self - .retrier - .retry(execution::create_retry_stream( - self.client.clone(), - components, - )) - .await - { - Ok(response) => Ok(response), - Err(e) => Err(azure_core::Error::new( - azure_core::error::ErrorKind::Other, - format!("Connection failed after retries: {e}"), - )), - } - } -} - -#[derive(MetricsComponent, Debug)] +#[derive(MetricsComponent)] pub struct AzureBlobStore { - client: Arc, + client: Arc, now_fn: NowFn, #[metric(help = "The container name for the Azure store")] container: String, @@ -428,6 +89,16 @@ pub struct AzureBlobStore { max_concurrent_uploads: usize, } +impl core::fmt::Debug for AzureBlobStore { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("AzureBlobStore") + .field("container", &self.container) + .field("blob_prefix", &self.blob_prefix) + .field("consider_expired_after_s", &self.consider_expired_after_s) + .finish_non_exhaustive() + } +} + impl AzureBlobStore where I: InstantWrapper, @@ -435,35 +106,80 @@ where { pub async fn new(spec: &ExperimentalAzureSpec, now_fn: NowFn) -> Result, Error> { let jitter_fn = spec.common.retry.make_jitter_fn(); + let client = Self::build_container_client(spec)?; + Self::new_with_client_and_jitter(spec, client, jitter_fn, now_fn) + } - let http_client = Arc::new( - AzureClient::new(spec.clone(), jitter_fn.clone()) - .map_err(|e| make_err!(Code::Unavailable, "Failed to create Azure client: {e}"))?, - ); + /// Builds the container URL and selects the auth strategy: + /// * `sas_url` set -> use it verbatim as the container URL with no credential. + /// * otherwise -> `https://{account}.{endpoint}/{container}` authenticated with + /// Entra ID via Workload Identity (keyless). + fn build_container_client(spec: &ExperimentalAzureSpec) -> Result { + let mut options = BlobContainerClientOptions::default(); + options.client_options.retry = RetryOptions::none(); + // Hand the SDK an HTTP client with an explicit rustls (ring) config. + options.client_options.transport = Some(Self::build_http_transport()?); + + let (container_url, credential): (Url, Option>) = + if let Some(sas_url) = spec.sas_url.as_ref() { + let url = Url::parse(sas_url) + .map_err(|e| make_err!(Code::InvalidArgument, "Invalid Azure sas_url: {e}"))?; + (url, None) + } else { + let endpoint = spec.endpoint.clone().unwrap_or_else(|| { + format!( + "https://{}.{DEFAULT_BLOB_ENDPOINT_SUFFIX}", + spec.account_name + ) + }); + let mut url = Url::parse(&endpoint) + .map_err(|e| make_err!(Code::InvalidArgument, "Invalid Azure endpoint: {e}"))?; + url.path_segments_mut() + .map_err(|()| { + make_err!( + Code::InvalidArgument, + "Azure endpoint is not a valid base URL: {endpoint}" + ) + })? + .pop_if_empty() + .push(&spec.container); + let credential: Arc = WorkloadIdentityCredential::new(None) + .map_err(|e| { + make_err!( + Code::FailedPrecondition, + "Failed to create Azure Workload Identity credential: {e}" + ) + })?; + (url, Some(credential)) + }; - let transport_options = TransportOptions::new(http_client); + BlobContainerClient::new(container_url, credential, Some(options)) + .map_err(|e| make_err!(Code::Unavailable, "Failed to create Azure client: {e}")) + } - let account_key = std::env::var(ACCOUNT_KEY_ENV_VAR).map_err(|e| { - make_err!( - Code::FailedPrecondition, - "Failed to read {ACCOUNT_KEY_ENV_VAR} environment variable: {e}" - ) - })?; + /// Builds an HTTP transport for the Azure SDK backed by a reqwest client with + /// an explicit rustls config using `NativeLink`'s ring crypto provider, so the + /// SDK never falls back to guessing a provider (which breaks HTTPS here). + fn build_http_transport() -> Result { + install_default_rustls_crypto_provider(); - let storage_credentials = - StorageCredentials::access_key(spec.account_name.clone(), Secret::new(account_key)); + let mut roots = rustls::RootCertStore::empty(); + roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + let tls_config = rustls::ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); - // Create a container client with the specified credentials and transport - let container_client = BlobServiceClient::builder(&spec.account_name, storage_credentials) - .transport(transport_options) - .container_client(&spec.container); + let client = reqwest::Client::builder() + .use_preconfigured_tls(tls_config) + .build() + .map_err(|e| make_err!(Code::Unavailable, "Failed to build Azure HTTP client: {e}"))?; - Self::new_with_client_and_jitter(spec, container_client, jitter_fn, now_fn) + Ok(Transport::new(Arc::new(client))) } pub fn new_with_client_and_jitter( spec: &ExperimentalAzureSpec, - client: ContainerClient, + client: BlobContainerClient, jitter_fn: Arc Duration + Send + Sync>, now_fn: NowFn, ) -> Result, Error> { @@ -504,13 +220,28 @@ where self.retrier .retry(unfold((), move |state| { let blob_path = blob_path.clone(); + let client = Arc::clone(&self.client); async move { - let result = self.client.blob_client(&blob_path).get_properties().await; + let _permit = match fs::get_permit().await { + Ok(permit) => permit, + Err(e) => { + return Some(( + RetryResult::Retry(make_err!( + Code::Unavailable, + "Failed to acquire permit: {e}" + )), + state, + )); + } + }; + + let result = client.blob_client(&blob_path).get_properties(None).await; match result { Ok(props) => { - if self.consider_expired_after_s > 0 { - let last_modified = props.blob.properties.last_modified; + if self.consider_expired_after_s > 0 + && let Some(last_modified) = props.last_modified().ok().flatten() + { let now = (self.now_fn)().unix_timestamp() as i64; if last_modified.unix_timestamp() + self.consider_expired_after_s <= now @@ -518,30 +249,33 @@ where return Some((RetryResult::Ok(None), state)); } } - let blob_size = props.blob.properties.content_length; + let blob_size = props.content_length().ok().flatten().unwrap_or(0); Some((RetryResult::Ok(Some(blob_size)), state)) } Err(err) => { - if err - .as_http_error() - .is_some_and(|e| e.status() == StatusCode::NotFound) - { + if err.http_status() == Some(StatusCode::NotFound) { + // Distinguish a missing container (a config error) from a + // missing blob (a normal cache miss). + if let ErrorKind::HttpResponse { + error_code: Some(error_code), + .. + } = err.kind() + && error_code == StorageErrorCode::ContainerNotFound.as_ref() + { + return Some(( + RetryResult::Err(make_err!( + Code::InvalidArgument, + "Container not found: {err}" + )), + state, + )); + } Some((RetryResult::Ok(None), state)) - } else if err.to_string().contains("ContainerNotFound") { - Some(( - RetryResult::Err(make_err!( - Code::InvalidArgument, - "Container not found: {}", - err - )), - state, - )) } else { Some(( RetryResult::Retry(make_err!( Code::Unavailable, - "Failed to get blob properties: {:?}", - err + "Failed to get blob properties: {err:?}" )), state, )) @@ -604,7 +338,7 @@ where UploadSizeInfo::ExactSize(sz) | UploadSizeInfo::MaxSize(sz) => sz, }; - // For small files, we'll use single block upload + // For small files of a known size we buffer to `Bytes` and upload in a single request. if max_size < DEFAULT_BLOCK_SIZE && matches!(upload_size, UploadSizeInfo::ExactSize(_)) { let UploadSizeInfo::ExactSize(sz) = upload_size else { unreachable!("upload_size must be UploadSizeInfo::ExactSize here"); @@ -615,67 +349,89 @@ where .err_tip(|| "Could not convert max_retry_buffer_per_request to u64")?, ); - return self.retrier + return self + .retrier .retry(unfold(reader, move |mut reader| { let client = Arc::clone(&self.client); - let blob_client = client.blob_client(&blob_path); + let blob_path = blob_path.clone(); async move { + let _permit = match fs::get_permit().await { + Ok(permit) => permit, + Err(e) => { + return Some(( + RetryResult::Retry(make_err!( + Code::Unavailable, + "Failed to acquire permit: {e}" + )), + reader, + )); + } + }; + let (mut tx, mut rx) = make_buf_channel_pair(); let result = { let reader_ref = &mut reader; let (upload_res, bind_res) = tokio::join!( - async { - let mut buffer = Vec::with_capacity(usize::try_from(sz).expect("size must be non-negative and fit in usize")); - while let Ok(Some(chunk)) = rx.try_next().await { - buffer.extend_from_slice(&chunk); - } + async { + let mut buffer = Vec::with_capacity( + usize::try_from(sz).expect( + "size must be non-negative and fit in usize", + ), + ); + while let Ok(Some(chunk)) = rx.try_next().await { + buffer.extend_from_slice(&chunk); + } - blob_client - .put_block_blob(Body::from(buffer)) - .content_type("application/octet-stream") - .into_future() - .await - .map(|_| ()) - .map_err(|e| make_err!(Code::Aborted, "{:?}", e)) - }, - async { - tx.bind_buffered(reader_ref).await - } - ); + client + .blob_client(&blob_path) + .block_blob_client() + .upload(RequestContent::from(buffer), None) + .await + .map(|_| ()) + .map_err(|e| make_err!(Code::Aborted, "{e:?}")) + }, + async { tx.bind_buffered(reader_ref).await } + ); match (upload_res, bind_res) { - (Ok(size), Ok(())) => Ok(size), + (Ok(()), Ok(())) => Ok(()), (Err(e), _) | (_, Err(e)) => Err(e), } .err_tip(|| "Failed to upload blob in single chunk") }; match result { - Ok(()) => Some((RetryResult::Ok(reader.get_bytes_received()), reader)), + Ok(()) => { + Some((RetryResult::Ok(reader.get_bytes_received()), reader)) + } Err(mut err) => { err.code = Code::Aborted; let bytes_received = reader.get_bytes_received(); if let Err(try_reset_err) = reader.try_reset_stream() { event!( - Level::ERROR, - ?bytes_received, - err = ?try_reset_err, - "Unable to reset stream after failed upload in AzureStore::update" - ); - Some((RetryResult::Err(err - .merge(try_reset_err) - .append(format!("Failed to retry upload with {bytes_received} bytes received in AzureStore::update"))), - reader)) + Level::ERROR, + ?bytes_received, + err = ?try_reset_err, + "Unable to reset stream after failed upload in AzureStore::update" + ); + Some(( + RetryResult::Err(err.merge(try_reset_err).append(format!( + "Failed to retry upload with {bytes_received} bytes received in AzureStore::update" + ))), + reader, + )) } else { - let err = err.append(format!("Retry on upload happened with {bytes_received} bytes received in AzureStore::update")); + let err = err.append(format!( + "Retry on upload happened with {bytes_received} bytes received in AzureStore::update" + )); event!( - Level::INFO, - ?err, - ?bytes_received, - "Retryable Azure error" - ); + Level::INFO, + ?err, + ?bytes_received, + "Retryable Azure error" + ); Some((RetryResult::Retry(err), reader)) } } @@ -685,12 +441,12 @@ where .await; } - // For larger files, we'll use block upload strategy + // For larger files we stream the content as staged blocks and commit a block list. let block_size = cmp::min(max_size / (MAX_BLOCKS as u64 - 1), MAX_BLOCK_SIZE).max(DEFAULT_BLOCK_SIZE); let (tx, mut rx) = mpsc::channel(self.max_concurrent_uploads); - let mut block_ids = Vec::with_capacity(MAX_BLOCKS); + let mut block_ids: Vec> = Vec::with_capacity(MAX_BLOCKS); let retrier = self.retrier.clone(); let read_stream_fut = { @@ -713,32 +469,48 @@ where total_uploaded += write_buf.len() as u64; - let block_id = format!("{block_id:032}"); + // Fixed-width, zero-padded ids keep the committed block list ordered + // after a lexicographic sort. + let block_id = format!("{block_id:032}").into_bytes(); let blob_path = blob_path.clone(); tx.send(async move { self.retrier .retry(unfold( - (write_buf, block_id.clone()), + (write_buf, block_id), move |(write_buf, block_id)| { let client = Arc::clone(&self.client); - let blob_client = client.blob_client(&blob_path); + let blob_path = blob_path.clone(); async move { - let retry_result = blob_client - .put_block( - block_id.clone(), - Body::from(write_buf.clone()), + let _permit = match fs::get_permit().await { + Ok(permit) => permit, + Err(e) => { + return Some(( + RetryResult::Retry(make_err!( + Code::Unavailable, + "Failed to acquire permit: {e}" + )), + (write_buf, block_id), + )); + } + }; + let content_length = write_buf.len() as u64; + let retry_result = client + .blob_client(&blob_path) + .block_blob_client() + .stage_block( + &block_id, + content_length, + RequestContent::from(write_buf.to_vec()), + None, ) - .into_future() .await .map_or_else( |e| { RetryResult::Retry(make_err!( - Code::Aborted, - "Failed to upload block {} in Azure store: {:?}", - block_id, - e - )) + Code::Aborted, + "Failed to upload block in Azure store: {e:?}" + )) }, |_| RetryResult::Ok(block_id.clone()), ); @@ -777,40 +549,61 @@ where } } - // Sorting block IDs to ensure consistent ordering + // Sorting block IDs to ensure consistent ordering of the committed blob. block_ids.sort_unstable(); - // Commit the block list - let block_list = BlockList { - blocks: block_ids - .into_iter() - .map(|id| BlobBlockType::Latest(BlockId::from(id))) - .collect(), + let block_list = BlockLookupList { + latest: Some(block_ids), + ..Default::default() }; retrier .retry(unfold(block_list, move |block_list| { let client = Arc::clone(&self.client); - let blob_client = client.blob_client(&blob_path); + let blob_path = blob_path.clone(); async move { - Some(( - blob_client - .put_block_list(block_list.clone()) - .content_type("application/octet-stream") - .into_future() - .await - .map_or_else( - |e| { - RetryResult::Retry( - Error::from_std_err(Code::Aborted, &e) - .append("Failed to commit block list in Azure store:"), - ) - }, - |_| RetryResult::Ok(total_uploaded), - ), - block_list, - )) + let _permit = match fs::get_permit().await { + Ok(permit) => permit, + Err(e) => { + return Some(( + RetryResult::Retry(make_err!( + Code::Unavailable, + "Failed to acquire permit: {e}" + )), + block_list, + )); + } + }; + + let blocks = match RequestContent::try_from(block_list.clone()) { + Ok(blocks) => blocks, + Err(e) => { + return Some(( + RetryResult::Err(make_err!( + Code::Internal, + "Failed to serialize block list in Azure store: {e:?}" + )), + block_list, + )); + } + }; + + let retry_result = client + .blob_client(&blob_path) + .block_blob_client() + .commit_block_list(blocks, None) + .await + .map_or_else( + |e| { + RetryResult::Retry( + Error::from_std_err(Code::Aborted, &e) + .append("Failed to commit block list in Azure store:"), + ) + }, + |_| RetryResult::Ok(total_uploaded), + ); + Some((retry_result, block_list)) } })) .await @@ -832,66 +625,65 @@ where let blob_path = self.make_blob_path(&key); - let client = Arc::clone(&self.client); - let blob_client = client.blob_client(&blob_path); let range = match length { - Some(len) => Range::new(offset, offset + len - 1), - None => Range::from(offset..), + Some(len) => Some(HttpRange::new(offset, len)), + None if offset == 0 => None, + None => Some(HttpRange::from_offset(offset)), }; self.retrier .retry(unfold(writer, move |writer| { - let range_clone = range.clone(); - let blob_client = blob_client.clone(); + let range = range.clone(); + let client = Arc::clone(&self.client); + let blob_path = blob_path.clone(); async move { - let result = async { - let mut stream = blob_client.get().range(range_clone.clone()).into_stream(); - - while let Some(chunk_result) = stream.next().await { - match chunk_result { - Ok(response) => { - let data = response.data.collect().await.map_err(|e| { - make_err!( - Code::Aborted, - "Failed to collect response data: {:?}", - e - ) - })?; - if data.is_empty() { - continue; - } - writer.send(data).await.map_err(|e| { - make_err!( - Code::Aborted, - "Failed to send data to writer: {:?}", - e - ) - })?; - } - Err(e) => { - return match e { - e if e.as_http_error().is_some_and(|e| { - e.status() == StatusCode::NotFound - }) => - { - Err(make_err!( - Code::NotFound, - "Blob not found in Azure: {:?}", - e - )) - } - _ => Err(make_err!( - Code::Aborted, - "Error reading from Azure stream: {:?}", - e - )), - }; + let _permit = match fs::get_permit().await { + Ok(permit) => permit, + Err(e) => { + return Some(( + RetryResult::Retry(make_err!( + Code::Unavailable, + "Failed to acquire permit: {e}" + )), + writer, + )); + } + }; + + let result: Result<(), Error> = async { + let options = BlobClientDownloadOptions { + range, + ..Default::default() + }; + let response = client + .blob_client(&blob_path) + .download(Some(options)) + .await + .map_err(|e| { + if e.http_status() == Some(StatusCode::NotFound) { + make_err!(Code::NotFound, "Blob not found in Azure: {e:?}") + } else { + make_err!( + Code::Aborted, + "Failed to start download from Azure: {e:?}" + ) } + })?; + + let mut body = response.body; + while let Some(chunk) = body.try_next().await.map_err(|e| { + make_err!(Code::Aborted, "Error reading from Azure stream: {e:?}") + })? { + if chunk.is_empty() { + continue; } + writer.send(chunk).await.map_err(|e| { + make_err!(Code::Aborted, "Failed to send data to writer: {e:?}") + })?; } writer.send_eof().map_err(|e| { - make_err!(Code::Aborted, "Failed to send EOF to writer: {:?}", e) + make_err!(Code::Aborted, "Failed to send EOF to writer: {e:?}") })?; Ok(()) } diff --git a/nativelink-store/tests/azure_blob_store_test.rs b/nativelink-store/tests/azure_blob_store_test.rs index e3ace7921..044e56c63 100644 --- a/nativelink-store/tests/azure_blob_store_test.rs +++ b/nativelink-store/tests/azure_blob_store_test.rs @@ -12,18 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::fmt::{Debug, Formatter}; use core::sync::atomic::{AtomicUsize, Ordering}; use core::time::Duration; +use std::collections::VecDeque; use std::sync::{Arc, Mutex}; -use azure_core::{HttpClient, StatusCode, TransportOptions, base64}; -use azure_storage::StorageCredentials; -use azure_storage_blobs::prelude::*; -use base64::encode; +use azure_core::error::ErrorKind; +use azure_core::http::headers::Headers; +use azure_core::http::{ + AsyncRawResponse, HttpClient, Method, Request, RetryOptions, StatusCode, Transport, Url, +}; +use azure_storage_blob::clients::{BlobContainerClient, BlobContainerClientOptions}; use bytes::{BufMut, Bytes, BytesMut}; -use nativelink_config::stores::ExperimentalAzureSpec; -use nativelink_error::{Error, ResultExt}; +use nativelink_config::stores::{CommonObjectSpec, ExperimentalAzureSpec, Retry}; +use nativelink_error::{Code, Error, ResultExt}; use nativelink_macro::nativelink_test; use nativelink_store::azure_blob_store::AzureBlobStore; use nativelink_util::buf_channel::make_buf_channel_pair; @@ -32,288 +34,315 @@ use nativelink_util::instant_wrapper::MockInstantWrapped; use nativelink_util::store_trait::{StoreKey, StoreLike, UploadSizeInfo}; use sha2::{Digest, Sha256}; -// Test constants const TEST_CONTAINER: &str = "test-container"; const TEST_ACCOUNT: &str = "testaccount"; -const TEST_KEY: &str = "dGVzdGtleQ=="; // base64 encoded "testkey" const TEST_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000"; const TEST_SIZE: u64 = 100; -type AzureBlobStoreTest = Arc MockInstantWrapped>>; +const EPOCH_LAST_MODIFIED: &str = "Thu, 01 Jan 1970 00:00:00 GMT"; -/// Test utilities -mod test_utils { - use core::default::Default; +type TestStore = Arc MockInstantWrapped>>; - use nativelink_config::stores::CommonObjectSpec; - - use super::*; +/// A single canned HTTP response the mock transport hands back to the SDK. +#[derive(Clone, Debug)] +struct CannedResponse { + status: StatusCode, + headers: Vec<(&'static str, String)>, + body: Vec, +} - #[derive(Debug)] - pub(crate) struct TestResponse { - pub status: StatusCode, - pub headers: Vec<(&'static str, String)>, - pub body: Vec, +fn properties_ok(content_length: u64) -> CannedResponse { + CannedResponse { + status: StatusCode::Ok, + headers: vec![ + ("content-length", content_length.to_string()), + ("last-modified", EPOCH_LAST_MODIFIED.to_string()), + ], + body: Vec::new(), } +} - impl TestResponse { - pub(crate) fn ok() -> Self { - Self { - status: StatusCode::Ok, - // A sample set of headers. - headers: vec![ - ("content-length", "0".to_string()), - ("last-modified", "Thu, 01 Jan 1970 00:00:00 GMT".to_string()), - ("etag", "\"test-etag\"".to_string()), - ( - "x-ms-creation-time", - "Thu, 01 Jan 1970 00:00:00 GMT".to_string(), - ), - ("x-ms-lease-status", "unlocked".to_string()), - ("x-ms-lease-state", "available".to_string()), - ("x-ms-blob-type", "BlockBlob".to_string()), - ("x-ms-server-encrypted", "true".to_string()), - ("x-ms-request-server-encrypted", "true".to_string()), - ("x-ms-blob-committed-block-count", "0".to_string()), - ("x-ms-blob-content-encoding", "utf-8".to_string()), - ( - "x-ms-request-id", - "00000000-0000-0000-0000-000000000000".to_string(), - ), - ("x-ms-version", "2020-04-08".to_string()), - ("date", "Thu, 01 Jan 1970 00:00:00 GMT".to_string()), - ], - body: vec![], - } - } - - pub(crate) const fn error(status: StatusCode) -> Self { - Self { - status, - headers: vec![], - body: vec![], - } - } - - pub(crate) const fn not_found() -> Self { - Self::error(StatusCode::NotFound) - } +const fn error_response(status: StatusCode) -> CannedResponse { + CannedResponse { + status, + headers: Vec::new(), + body: Vec::new(), + } +} - pub(crate) fn with_content_length(mut self, length: usize) -> Self { - if let Some(header) = self - .headers - .iter_mut() - .find(|(key, _)| *key == "content-length") - { - header.1 = length.to_string(); - } else { - self.headers.push(("content-length", length.to_string())); - } - self - } +const fn not_found() -> CannedResponse { + error_response(StatusCode::NotFound) +} - pub(crate) fn with_body(mut self, body: Vec) -> Self { - self.body = body; - self - } +/// Successful write result for the SDK upload/stage/commit operations. +const fn created() -> CannedResponse { + CannedResponse { + status: StatusCode::Created, + headers: Vec::new(), + body: Vec::new(), } +} - #[derive(Debug)] - pub(crate) struct TestRequest { - pub method: &'static str, - pub url_pattern: String, - pub response: TestResponse, +fn download_ok(status: StatusCode, body: Vec) -> CannedResponse { + CannedResponse { + status, + headers: vec![("content-length", body.len().to_string())], + body, } +} - impl TestRequest { - pub(crate) const fn new( - method: &'static str, - url_pattern: String, - response: TestResponse, - ) -> Self { - Self { - method, - url_pattern, - response, - } - } +/// Fake [`HttpClient`] injected into the SDK pipeline. It routes on the request's +/// method and the `comp` query parameter to mirror how the store calls the SDK: +/// * HEAD -> `get_properties` +/// * GET -> `download` (range is carried in a header) +/// * PUT `?comp=block` -> stage a block +/// * PUT `?comp=blocklist`-> commit the block list +/// * PUT (no `comp`) -> single-shot block-blob upload +/// +/// Each operation has its own queue of responses consumed in order, which lets the +/// retry tests assert the exact request sequence. +#[derive(Debug, Default)] +struct MockTransport { + properties: Mutex>, + download: Mutex>, + upload: Mutex>, + stage_block: Mutex>, + commit: Mutex>, + /// Raw XML body of the last `commit_block_list` request (the committed block + /// list), captured so tests can assert the exact set/order of block ids. + committed_block_list: Mutex>, + count: AtomicUsize, +} - pub(crate) fn head(digest: &str, size: u64, response: TestResponse) -> Self { - Self::new( - "HEAD", - format!("{TEST_CONTAINER}/{digest}-{size}"), - response, - ) - } +impl MockTransport { + fn new() -> Arc { + Arc::new(Self::default()) + } - pub(crate) fn get(digest: &str, size: u64, response: TestResponse) -> Self { - Self::new("GET", format!("{TEST_CONTAINER}/{digest}-{size}"), response) - } + fn push_properties(&self, response: CannedResponse) { + self.properties.lock().unwrap().push_back(response); + } - pub(crate) fn put(digest: &str, size: u64, response: TestResponse) -> Self { - Self::new("PUT", format!("{TEST_CONTAINER}/{digest}-{size}"), response) - } + fn push_download(&self, response: CannedResponse) { + self.download.lock().unwrap().push_back(response); } - #[derive(Clone)] - pub(crate) struct MockAzureClient { - expected_requests: Arc>, - current_request: Arc, - request_log: Arc>>, + fn push_upload(&self, response: CannedResponse) { + self.upload.lock().unwrap().push_back(response); } - impl Debug for MockAzureClient { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.debug_struct("MockAzureClient") - .field("current_request", &self.current_request) - .field("request_count", &self.request_log.lock().unwrap().len()) - .finish() - } + fn request_count(&self) -> usize { + self.count.load(Ordering::SeqCst) } - impl MockAzureClient { - pub(crate) fn new(requests: Vec) -> Self { - Self { - expected_requests: Arc::new(requests), - current_request: Arc::new(AtomicUsize::new(0)), - request_log: Arc::new(Mutex::new(Vec::new())), - } - } + fn committed_block_list(&self) -> Option { + self.committed_block_list.lock().unwrap().clone() + } - pub(crate) fn request_count(&self) -> usize { - self.request_log.lock().unwrap().len() - } + fn pop(queue: &Mutex>) -> Option { + queue.lock().unwrap().pop_front() + } +} - pub(crate) fn single_head_request(response: TestResponse) -> Self { - Self::new(vec![TestRequest::head(TEST_HASH, TEST_SIZE, response)]) - } +fn build_response(canned: CannedResponse) -> AsyncRawResponse { + let mut headers = Headers::new(); + for (key, value) in canned.headers { + headers.insert(key, value); } + AsyncRawResponse::from_bytes(canned.status, headers, canned.body) +} - #[async_trait::async_trait] - impl HttpClient for MockAzureClient { - async fn execute_request( - &self, - request: &azure_core::Request, - ) -> azure_core::Result { - self.request_log.lock().unwrap().push(request.clone()); - - let index = self.current_request.fetch_add(1, Ordering::SeqCst); - let expected = &self.expected_requests[index]; - - if request.method().to_string() != expected.method - || !request.url().as_str().contains(&expected.url_pattern) - { - return Err(azure_core::Error::new( - azure_core::error::ErrorKind::Other, - format!("Unexpected request: {} {}", request.method(), request.url()), - )); - } +#[async_trait::async_trait] +impl HttpClient for MockTransport { + async fn execute_request(&self, request: &Request) -> azure_core::Result { + self.count.fetch_add(1, Ordering::SeqCst); - let mut headers = azure_core::headers::Headers::new(); - for (key, value) in &expected.response.headers { - headers.insert(*key, value); + let query = request.url().query().unwrap_or_default().to_owned(); + let canned = match request.method() { + Method::Head => Self::pop(&self.properties).unwrap_or_else(|| properties_ok(0)), + Method::Get => { + Self::pop(&self.download).unwrap_or_else(|| download_ok(StatusCode::Ok, Vec::new())) } + Method::Put if query.contains("comp=blocklist") => { + *self.committed_block_list.lock().unwrap() = Some(Bytes::from(request.body())); + Self::pop(&self.commit).unwrap_or_else(created) + } + Method::Put if query.contains("comp=block") => { + Self::pop(&self.stage_block).unwrap_or_else(created) + } + Method::Put => Self::pop(&self.upload).unwrap_or_else(created), + other => { + return Err(azure_core::Error::with_message( + ErrorKind::Other, + format!("Unexpected request: {other} {}", request.url()), + )); + } + }; - let body = expected.response.body.clone(); - Ok(azure_core::Response::new( - expected.response.status, - headers, - Box::pin(futures::stream::once(async move { Ok(Bytes::from(body)) })), - )) - } + Ok(build_response(canned)) } +} - pub(crate) fn create_test_store( - client: MockAzureClient, - retry_config: Option, - consider_expired_after_s: Option, - ) -> Result { - let transport = TransportOptions::new(Arc::new(client)); - let credentials = StorageCredentials::access_key( - TEST_ACCOUNT.to_string(), - azure_core::auth::Secret::new(TEST_KEY.to_string()), - ); - - let container_client = BlobServiceClient::builder(TEST_ACCOUNT, credentials) - .transport(transport) - .container_client(TEST_CONTAINER); - - let spec = ExperimentalAzureSpec { - account_name: TEST_ACCOUNT.to_string(), - container: TEST_CONTAINER.to_string(), - common: CommonObjectSpec { - consider_expired_after_s: consider_expired_after_s.unwrap_or(0), - retry: retry_config.unwrap_or_default(), - ..Default::default() - }, +fn create_test_store( + mock: Arc, + retry: Option, + consider_expired_after_s: Option, +) -> Result { + let mut options = BlobContainerClientOptions::default(); + // NativeLink's own `Retrier` wraps each operation, so disable the SDK's retry + // policy to keep request counts deterministic. + options.client_options.retry = RetryOptions::none(); + options.client_options.transport = Some(Transport::new(mock)); + + let container_url = Url::parse(&format!( + "https://{TEST_ACCOUNT}.blob.core.windows.net/{TEST_CONTAINER}" + )) + .expect("valid container URL"); + let client = BlobContainerClient::new(container_url, None, Some(options)) + .expect("failed to build BlobContainerClient"); + + let spec = ExperimentalAzureSpec { + account_name: TEST_ACCOUNT.to_string(), + container: TEST_CONTAINER.to_string(), + common: CommonObjectSpec { + consider_expired_after_s: consider_expired_after_s.unwrap_or(0), + retry: retry.unwrap_or_default(), ..Default::default() - }; - - AzureBlobStore::new_with_client_and_jitter( - &spec, - container_client, - Arc::new(|_| Duration::from_secs(0)), - MockInstantWrapped::default, - ) - } + }, + ..Default::default() + }; - pub(crate) fn create_test_digest() -> Result { - DigestInfo::try_new(TEST_HASH, TEST_SIZE) - } + AzureBlobStore::new_with_client_and_jitter( + &spec, + client, + Arc::new(|_| Duration::from_secs(0)), + MockInstantWrapped::default, + ) } -use test_utils::*; +fn create_test_digest() -> Result { + DigestInfo::try_new(TEST_HASH, TEST_SIZE) +} #[nativelink_test] async fn test_has_object_found() -> Result<(), Error> { - let client = MockAzureClient::single_head_request(TestResponse::ok().with_content_length(512)); - let store = create_test_store(client.clone(), None, None)?; + let mock = MockTransport::new(); + mock.push_properties(properties_ok(512)); + let store = create_test_store(Arc::clone(&mock), None, None)?; let result = store.has(create_test_digest()?).await?; assert_eq!(result, Some(512)); - assert_eq!(client.request_count(), 1); + assert_eq!(mock.request_count(), 1); Ok(()) } #[nativelink_test] async fn test_has_object_not_found() -> Result<(), Error> { - let client = MockAzureClient::single_head_request(TestResponse::not_found()); - let store = create_test_store(client.clone(), None, None)?; + let mock = MockTransport::new(); + mock.push_properties(not_found()); + let store = create_test_store(Arc::clone(&mock), None, None)?; let result = store.has(create_test_digest()?).await?; assert_eq!(result, None); - assert_eq!(client.request_count(), 1); + assert_eq!(mock.request_count(), 1); Ok(()) } #[nativelink_test] async fn test_has_with_retries() -> Result<(), Error> { - let client = MockAzureClient::new(vec![ - TestRequest::head( - TEST_HASH, - TEST_SIZE, - TestResponse::error(StatusCode::InternalServerError), - ), - TestRequest::head( - TEST_HASH, - TEST_SIZE, - TestResponse::ok().with_content_length(111), - ), - ]); - - let retry_config = nativelink_config::stores::Retry { + let mock = MockTransport::new(); + mock.push_properties(error_response(StatusCode::InternalServerError)); + mock.push_properties(properties_ok(111)); + + let retry = Retry { max_retries: 3, delay: 0.0, jitter: 0.0, ..Default::default() }; - let store = create_test_store(client.clone(), Some(retry_config), None)?; + let store = create_test_store(Arc::clone(&mock), Some(retry), None)?; let result = store.has(create_test_digest()?).await?; assert_eq!(result, Some(111)); - assert_eq!(client.request_count(), 2); + assert_eq!(mock.request_count(), 2); + Ok(()) +} + +#[nativelink_test] +async fn test_has_with_results_zero_digest() -> Result<(), Error> { + let digest = DigestInfo::new(Sha256::new().finalize().into(), 0); + let keys = vec![StoreKey::from(&digest)]; + let mut results = vec![None]; + + let mock = MockTransport::new(); + let store = create_test_store(Arc::clone(&mock), None, None)?; + + store.has_with_results(&keys, &mut results).await?; + + assert_eq!(results, vec![Some(0)]); + assert_eq!( + mock.request_count(), + 0, + "Expected no requests for zero digest" + ); + Ok(()) +} + +#[nativelink_test] +async fn test_has_with_expired_result() -> Result<(), Error> { + use mock_instant::thread_local::MockClock; + + const CONTENT_SIZE: usize = 10; + + let mock = MockTransport::new(); + mock.push_properties(properties_ok(512)); + mock.push_properties(properties_ok(512)); + + let store = create_test_store( + Arc::clone(&mock), + Some(Retry { + max_retries: 1, + delay: 0.0, + jitter: 0.0, + ..Default::default() + }), + Some(2 * 24 * 60 * 60), + )?; + + // Time starts at 1970-01-01 00:00:00 and the blob is last-modified at the epoch. + let digest = DigestInfo::try_new(TEST_HASH, CONTENT_SIZE)?; + + // 1 day in: not expired. + { + MockClock::advance(Duration::from_secs(24 * 60 * 60)); + let mut results = vec![None]; + store + .has_with_results(&[digest.into()], &mut results) + .await?; + assert_eq!( + results, + vec![Some(512)], + "Should find non-expired content after 1 day" + ); + } + + // 4 days in: expired (older than the 2-day threshold). + { + MockClock::advance(Duration::from_secs(3 * 24 * 60 * 60)); + let mut results = vec![None]; + store + .has_with_results(&[digest.into()], &mut results) + .await?; + assert_eq!( + results, + vec![None], + "Should not find expired content after 4 days" + ); + } + + assert_eq!(mock.request_count(), 2); Ok(()) } @@ -321,19 +350,55 @@ async fn test_has_with_retries() -> Result<(), Error> { async fn test_get() -> Result<(), Error> { const VALUE: &str = "test_content"; - let client = MockAzureClient::new(vec![TestRequest::get( - TEST_HASH, - TEST_SIZE, - TestResponse::ok().with_body(VALUE.as_bytes().to_vec()), - )]); + let mock = MockTransport::new(); + mock.push_download(download_ok(StatusCode::Ok, VALUE.as_bytes().to_vec())); - let store = create_test_store(client.clone(), None, None)?; + let store = create_test_store(Arc::clone(&mock), None, None)?; let result = store .get_part_unchunked(create_test_digest()?, 0, None) .await?; assert_eq!(result, VALUE.as_bytes()); - assert_eq!(client.request_count(), 1); + assert_eq!(mock.request_count(), 1); + Ok(()) +} + +#[nativelink_test] +async fn test_get_byte_range() -> Result<(), Error> { + const VALUE: &str = "0123456789abcdef"; + const OFFSET: u64 = 4; + const LENGTH: u64 = 6; + let start = usize::try_from(OFFSET).unwrap(); + let end = usize::try_from(OFFSET + LENGTH).unwrap(); + let expected = &VALUE.as_bytes()[start..end]; + + let mock = MockTransport::new(); + // A 206 Partial Content with just the requested slice as the body. + mock.push_download(download_ok(StatusCode::PartialContent, expected.to_vec())); + + let store = create_test_store(Arc::clone(&mock), None, None)?; + let result = store + .get_part_unchunked(create_test_digest()?, OFFSET, Some(LENGTH)) + .await?; + + assert_eq!(result, expected); + assert_eq!(mock.request_count(), 1); + Ok(()) +} + +#[nativelink_test] +async fn test_get_not_found() -> Result<(), Error> { + let mock = MockTransport::new(); + mock.push_download(not_found()); + + let store = create_test_store(Arc::clone(&mock), None, None)?; + let err = store + .get_part_unchunked(create_test_digest()?, 0, None) + .await + .expect_err("expected a NotFound error"); + + assert_eq!(err.code, Code::NotFound); + assert_eq!(mock.request_count(), 1); Ok(()) } @@ -341,72 +406,74 @@ async fn test_get() -> Result<(), Error> { async fn test_get_with_retries() -> Result<(), Error> { const VALUE: &str = "test_content"; - let client = MockAzureClient::new(vec![ - TestRequest::get( - TEST_HASH, - TEST_SIZE, - TestResponse::error(StatusCode::InternalServerError), - ), - TestRequest::get( - TEST_HASH, - TEST_SIZE, - TestResponse::error(StatusCode::ServiceUnavailable), - ), - TestRequest::get( - TEST_HASH, - TEST_SIZE, - TestResponse::error(StatusCode::Conflict), - ), - TestRequest::get( - TEST_HASH, - TEST_SIZE, - TestResponse::ok().with_body(VALUE.as_bytes().to_vec()), - ), - ]); - - let retry_config = nativelink_config::stores::Retry { + let mock = MockTransport::new(); + mock.push_download(error_response(StatusCode::InternalServerError)); + mock.push_download(error_response(StatusCode::ServiceUnavailable)); + mock.push_download(error_response(StatusCode::Conflict)); + mock.push_download(download_ok(StatusCode::Ok, VALUE.as_bytes().to_vec())); + + let retry = Retry { max_retries: 1024, delay: 0.0, jitter: 0.0, ..Default::default() }; - let store = create_test_store(client.clone(), Some(retry_config), None)?; + let store = create_test_store(Arc::clone(&mock), Some(retry), None)?; let result = store .get_part_unchunked(create_test_digest()?, 0, None) .await?; assert_eq!(result, VALUE.as_bytes()); - assert_eq!(client.request_count(), 4); + assert_eq!(mock.request_count(), 4); + Ok(()) +} + +#[nativelink_test] +async fn test_get_part_zero_digest() -> Result<(), Error> { + let digest = DigestInfo::new(Sha256::new().finalize().into(), 0); + let mock = MockTransport::new(); + let store = create_test_store(Arc::clone(&mock), None, None)?; + let (mut writer, mut reader) = make_buf_channel_pair(); + + let (get_result, file_data) = tokio::join!( + store.get_part(digest, &mut writer, 0, None), + reader.consume(Some(1024)) + ); + + get_result?; + let file_data = file_data.err_tip(|| "Error reading bytes")?; + + assert_eq!(file_data.len(), 0, "Expected empty file content"); + assert_eq!( + mock.request_count(), + 0, + "Expected no requests for zero digest" + ); Ok(()) } #[nativelink_test] async fn test_update_small_file() -> Result<(), Error> { - const CONTENT_LENGTH: usize = 1024; // Small enough for single block upload (<5MB). + const CONTENT_LENGTH: usize = 1024; // Below DEFAULT_BLOCK_SIZE: single-shot upload. let mut send_data = BytesMut::new(); for i in 0..CONTENT_LENGTH { send_data.put_u8(u8::try_from((i % 93) + 33).unwrap()); } let send_data = send_data.freeze(); - let client = MockAzureClient::new(vec![TestRequest::put( - TEST_HASH, - TEST_SIZE, - TestResponse::ok(), - )]); + let mock = MockTransport::new(); + mock.push_upload(created()); - let store = create_test_store(client.clone(), None, None)?; + let store = create_test_store(Arc::clone(&mock), None, None)?; let (mut tx, rx) = make_buf_channel_pair(); - // Starting the update futures let update_fut = store.update( create_test_digest()?, rx, UploadSizeInfo::ExactSize(CONTENT_LENGTH as u64), ); - // Sending the data in smaller chunks to test streaming let send_data_copy = send_data.clone(); let send_fut = Box::pin(async move { const CHUNK_SIZE: usize = 256; @@ -416,23 +483,21 @@ async fn test_update_small_file() -> Result<(), Error> { tx.send_eof() }); - // Waiting for both futures to complete let (update_result, send_result) = tokio::join!(update_fut, send_fut); update_result?; send_result?; - assert_eq!(client.request_count(), 1); + assert_eq!(mock.request_count(), 1); Ok(()) } #[nativelink_test] async fn test_update_zero_size() -> Result<(), Error> { - let client = MockAzureClient::new(vec![]); // Should not make any requests - let store = create_test_store(client.clone(), None, None)?; + let mock = MockTransport::new(); + let store = create_test_store(Arc::clone(&mock), None, None)?; let (mut tx, rx) = make_buf_channel_pair(); let update_fut = store.update(create_test_digest()?, rx, UploadSizeInfo::ExactSize(0)); - let send_fut = async move { tx.send_eof() }; let (update_result, send_result) = tokio::join!(update_fut, send_fut); @@ -440,7 +505,7 @@ async fn test_update_zero_size() -> Result<(), Error> { update_result?; send_result?; assert_eq!( - client.request_count(), + mock.request_count(), 0, "Zero-size upload should not make any requests" ); @@ -457,23 +522,18 @@ async fn test_update_with_retries() -> Result<(), Error> { } let send_data = send_data.freeze(); - let client = MockAzureClient::new(vec![ - TestRequest::put( - TEST_HASH, - TEST_SIZE, - TestResponse::error(StatusCode::InternalServerError), - ), - TestRequest::put(TEST_HASH, TEST_SIZE, TestResponse::ok()), - ]); + let mock = MockTransport::new(); + mock.push_upload(error_response(StatusCode::InternalServerError)); + mock.push_upload(created()); - let retry_config = nativelink_config::stores::Retry { + let retry = Retry { max_retries: 3, delay: 0.0, jitter: 0.0, ..Default::default() }; - let store = create_test_store(client.clone(), Some(retry_config), None)?; + let store = create_test_store(Arc::clone(&mock), Some(retry), None)?; let (mut tx, rx) = make_buf_channel_pair(); let update_fut = store.update( @@ -492,178 +552,50 @@ async fn test_update_with_retries() -> Result<(), Error> { update_result?; send_result?; - assert_eq!(client.request_count(), 2); + assert_eq!(mock.request_count(), 2); Ok(()) } #[nativelink_test] async fn test_multipart_upload_large_file() -> Result<(), Error> { - const MIN_BLOCK_SIZE: usize = 5 * 1024 * 1024; // 5MB + const MIN_BLOCK_SIZE: usize = 5 * 1024 * 1024; // 5 MiB DEFAULT_BLOCK_SIZE. const TOTAL_SIZE: usize = MIN_BLOCK_SIZE * 2 + 50; - const DIGEST_STR: &str = TEST_HASH; let mut send_data = Vec::with_capacity(TOTAL_SIZE); for i in 0..TOTAL_SIZE { send_data.push(u8::try_from((i * 3) % 256).unwrap()); } - // Generate and manually URL-encode Base64-encoded block IDs - let block_ids: Vec = (0..3) - .map(|i| { - let block_id = format!("{i:032}"); - let base64_encoded = encode(block_id); - base64_encoded - .replace('=', "%3D") - .replace('+', "%2B") - .replace('/', "%2F") - }) - .collect(); - - let client = MockAzureClient::new(vec![ - TestRequest::new( - "PUT", - format!( - "{TEST_CONTAINER}/{DIGEST_STR}-{TOTAL_SIZE}?blockid={}&comp=block", - block_ids[0] - ), - TestResponse::ok(), - ), - TestRequest::new( - "PUT", - format!( - "{TEST_CONTAINER}/{DIGEST_STR}-{TOTAL_SIZE}?blockid={}&comp=block", - block_ids[1] - ), - TestResponse::ok(), - ), - TestRequest::new( - "PUT", - format!( - "{TEST_CONTAINER}/{DIGEST_STR}-{TOTAL_SIZE}?blockid={}&comp=block", - block_ids[2] - ), - TestResponse::ok(), - ), - TestRequest::new( - "PUT", - format!("{TEST_CONTAINER}/{DIGEST_STR}-{TOTAL_SIZE}?comp=blocklist"), - TestResponse::ok(), - ), - ]); - - let store = create_test_store(client.clone(), None, None)?; - let digest = DigestInfo::try_new(DIGEST_STR, TOTAL_SIZE)?; + // No queued responses: the mock returns 201 for every staged block and the + // final commit. The 10 MiB payload splits into three 5-MiB-or-less blocks. + let mock = MockTransport::new(); + let store = create_test_store(Arc::clone(&mock), None, None)?; + let digest = DigestInfo::try_new(TEST_HASH, TOTAL_SIZE)?; let store_key: StoreKey = StoreKey::from(&digest); store.update_oneshot(store_key, send_data.into()).await?; - assert_eq!(client.request_count(), 4); - Ok(()) -} - -#[nativelink_test] -async fn test_get_part_zero_digest() -> Result<(), Error> { - let digest = DigestInfo::new(Sha256::new().finalize().into(), 0); - let client = MockAzureClient::new(vec![]); - let store = Arc::new(create_test_store(client.clone(), None, None)?); - let (mut writer, mut reader) = make_buf_channel_pair(); - let (get_result, file_data) = tokio::join!( - store.get_part(digest, &mut writer, 0, None), - reader.consume(Some(1024)) - ); - - get_result?; - let file_data = file_data.err_tip(|| "Error reading bytes")?; - - assert_eq!(file_data.len(), 0, "Expected empty file content"); - assert_eq!( - client.request_count(), - 0, - "Expected no requests for zero digest" - ); - Ok(()) -} -#[nativelink_test] -async fn test_has_with_results_zero_digests() -> Result<(), Error> { - let digest = DigestInfo::new(Sha256::new().finalize().into(), 0); - let store_key: StoreKey = StoreKey::from(&digest); - let keys = vec![store_key]; - let mut results = vec![None]; - - let client = MockAzureClient::new(vec![]); // Should make no requests - let store = create_test_store(client.clone(), None, None)?; - - store.has_with_results(&keys, &mut results).await?; - - assert_eq!(results, vec![Some(0)]); - assert_eq!( - client.request_count(), - 0, - "Expected no requests for zero digest" - ); - Ok(()) -} - -#[nativelink_test] -async fn test_has_with_expired_result() -> Result<(), Error> { - const CONTENT_SIZE: usize = 10; - use mock_instant::thread_local::MockClock; - - let client = MockAzureClient::new(vec![ - TestRequest::head( - TEST_HASH, - CONTENT_SIZE as u64, - TestResponse::ok().with_content_length(512), - ), - TestRequest::head( - TEST_HASH, - CONTENT_SIZE as u64, - TestResponse::ok().with_content_length(512), - ), - ]); - - let store = create_test_store( - client.clone(), - Some(nativelink_config::stores::Retry { - max_retries: 1, - delay: 0.0, - jitter: 0.0, - ..Default::default() - }), - Some(2 * 24 * 60 * 60), - )?; - - // Time starts at 1970-01-01 00:00:00 - let digest = DigestInfo::try_new(TEST_HASH, CONTENT_SIZE)?; - - // Check at 1 day (Not expired) - { - MockClock::advance(Duration::from_secs(24 * 60 * 60)); - let mut results = vec![None]; - store - .has_with_results(&[digest.into()], &mut results) - .await?; - assert_eq!( - results, - vec![Some(512)], - "Should find non-expired content after 1 day" - ); - } - - // Check at 3 days (expired) - { - MockClock::advance(Duration::from_secs(3 * 24 * 60 * 60)); - let mut results = vec![None]; - store - .has_with_results(&[digest.into()], &mut results) - .await?; - assert_eq!( - results, - vec![None], - "Should not find expired content after 3 days" + // 3 staged blocks + 1 committed block list. + assert_eq!(mock.request_count(), 4); + + // The store commits a block list of fixed-width, zero-padded block ids. Assert + // the committed XML carries exactly those ids, base64-encoded, in order. + let committed = mock + .committed_block_list() + .expect("commit_block_list must have been called"); + let committed = core::str::from_utf8(&committed).expect("committed block list must be utf-8"); + + let mut last_pos = 0; + for block_id in 0..3 { + let encoded = azure_core::base64::encode(format!("{block_id:032}").into_bytes()); + let pos = committed.find(&encoded).unwrap_or_else(|| { + panic!("block id {block_id} ({encoded}) missing from committed list: {committed}") + }); + assert!( + pos >= last_pos, + "block ids must be committed in ascending order" ); + last_pos = pos; } - - assert_eq!(client.request_count(), 2); Ok(()) } diff --git a/nativelink-store/tests/mongo_runner/downloader.rs b/nativelink-store/tests/mongo_runner/downloader.rs index 967cf884a..40da54095 100644 --- a/nativelink-store/tests/mongo_runner/downloader.rs +++ b/nativelink-store/tests/mongo_runner/downloader.rs @@ -122,6 +122,12 @@ where use std::fs::File; use std::io::Write; + // reqwest is built with `rustls-no-provider`, so it panics when building a + // client unless a rustls crypto provider is installed as the process default. + // The server binary installs one at startup; this download runs before any + // store does, so install it here too. Idempotent: ignores the already-set case. + drop(rustls::crypto::ring::default_provider().install_default()); + let response = reqwest::get(url).await?; let total = response.content_length(); diff --git a/nativelink-test/fuzz/Cargo.lock b/nativelink-test/fuzz/Cargo.lock index 4d675ba42..c5469f6bf 100644 --- a/nativelink-test/fuzz/Cargo.lock +++ b/nativelink-test/fuzz/Cargo.lock @@ -1396,9 +1396,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64", "bytes", @@ -1412,9 +1412,6 @@ dependencies = [ "log", "percent-encoding", "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", "tower", @@ -1602,18 +1599,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_with" version = "3.21.0" diff --git a/web/apps/docs/content/docs/reference/nativelink-config/main.mdx b/web/apps/docs/content/docs/reference/nativelink-config/main.mdx index 02431ba98..512981d5e 100644 --- a/web/apps/docs/content/docs/reference/nativelink-config/main.mdx +++ b/web/apps/docs/content/docs/reference/nativelink-config/main.mdx @@ -5,7 +5,7 @@ full: true --- {/* AUTOGENERATED — do not edit by hand. - Source: nativelink-config @ main (ce3a919e) + Source: nativelink-config @ main (ef787974) Regenerate from web/: bun --filter @nativelink/docs gen:config-reference */} @@ -218,8 +218,8 @@ It supports the following backends: "namespace": "your-object-storage-namespace", "region": "us-phoenix-1", "bucket": "nativelink-cas", - "access_key_id": "${OCI_ACCESS_KEY_ID}", - "secret_access_key": "${OCI_SECRET_ACCESS_KEY}", + "access_key_id": "oci_access_key_id", + "secret_access_key": "oci_secret_access_key", "key_prefix": "test-prefix/", "retry": { "max_retries": 6, @@ -1201,8 +1201,8 @@ It supports the following backends: "namespace": "your-object-storage-namespace", "region": "us-phoenix-1", "bucket": "nativelink-cas", - "access_key_id": "${OCI_ACCESS_KEY_ID}", - "secret_access_key": "${OCI_SECRET_ACCESS_KEY}", + "access_key_id": "oci_access_key_id", + "secret_access_key": "oci_secret_access_key", "key_prefix": "test-prefix/", "retry": { "max_retries": 6, From d55e0e5dc6a4b81e35afc9aa3886f412e28a8702 Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Tue, 7 Jul 2026 00:31:47 +0530 Subject: [PATCH 022/144] ci(release): build aarch64 Linux binary on a native arm64 runner (#2483) * ci(release): build the aarch64 Linux binary on a native arm64 runner * ci(release): drop x86_64 macOS target (Determinate Nix dropped Intel support) --- .github/workflows/release.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0024a1068..df3c12358 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -64,16 +64,14 @@ jobs: os: ubuntu-24.04 attr: nativelink-x86_64-linux - target: aarch64-unknown-linux-musl - os: ubuntu-24.04 + # Build natively on ARM64 so older tags build too: the aarch64 + # cross-compile fix (#2454) isn't in releases like v1.5.2, where + # cross-compiling fails on gnu/stubs-32.h. + os: ubuntu-24.04-arm attr: nativelink-aarch64-linux - target: aarch64-apple-darwin os: macos-26 attr: nativelink-aarch64-darwin - # macos-26-intel is the x86_64 macOS runner; drop this entry if Intel - # macOS runners are retired and x86_64 macOS binaries aren't required. - - target: x86_64-apple-darwin - os: macos-26-intel - attr: nativelink-x86_64-darwin runs-on: ${{ matrix.os }} timeout-minutes: 90 env: From f39ea3a178e5914300ced5fdd060af9c60b7d9e8 Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Tue, 7 Jul 2026 02:45:56 +0530 Subject: [PATCH 023/144] Release NativeLink v1.6.0 (#2512) --- CHANGELOG.md | 54 +++++++++++++++++++ Cargo.lock | 26 ++++----- Cargo.toml | 2 +- MODULE.bazel | 2 +- nativelink-config/Cargo.toml | 2 +- nativelink-error/Cargo.toml | 2 +- nativelink-macro/Cargo.toml | 2 +- nativelink-metric/Cargo.toml | 2 +- .../nativelink-metric-macro-derive/Cargo.toml | 2 +- nativelink-proto/Cargo.toml | 2 +- nativelink-redis-tester/Cargo.toml | 2 +- nativelink-scheduler/Cargo.toml | 2 +- nativelink-service/Cargo.toml | 2 +- nativelink-store/Cargo.toml | 2 +- nativelink-test/fuzz/Cargo.lock | 10 ++-- nativelink-util/Cargo.toml | 2 +- nativelink-worker/Cargo.toml | 2 +- 17 files changed, 86 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6bb9914c..1020c54f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,60 @@ All notable changes to this project will be documented in this file. +## [1.6.0](https://github.com/TraceMachina/nativelink/compare/v1.5.2..v1.6.0) - 2026-07-06 + +### ⛰️ Features + +- Add troubleshooting tips for macOS Nix setup ([#2505](https://github.com/TraceMachina/nativelink/issues/2505)) - ([5e72a32](https://github.com/TraceMachina/nativelink/commit/5e72a32f2b07f5ce5c6b03eded08c7380999cafe)) +- Regenerate Changelog on New Release ([#2500](https://github.com/TraceMachina/nativelink/issues/2500)) - ([503056f](https://github.com/TraceMachina/nativelink/commit/503056f16ddb13d5aabc76cc54b1af2032f4c384)) +- Add Oracle store ([#2492](https://github.com/TraceMachina/nativelink/issues/2492)) - ([8b74394](https://github.com/TraceMachina/nativelink/commit/8b7439435577a9465ced7229f16664438b607cd3)) +- Improve logging for detect_duplicate_upload and fake redis sync ([#2481](https://github.com/TraceMachina/nativelink/issues/2481)) - ([66f7159](https://github.com/TraceMachina/nativelink/commit/66f7159365cb5009e935d50920d0a5125bc336a8)) +- Add multi-arch images for worker-init ([#2459](https://github.com/TraceMachina/nativelink/issues/2459)) - ([a5b6f65](https://github.com/TraceMachina/nativelink/commit/a5b6f65f45e7c404dfc9233d249aca10eaae587b)) + +### 📚 Documentation + +- *(config-reference)* regenerate for NativeLink v1.5.2 ([#2447](https://github.com/TraceMachina/nativelink/issues/2447)) - ([9ec713c](https://github.com/TraceMachina/nativelink/commit/9ec713c8120b34ea46756a55a42c7079319ecd25)) +- Update the OCI store with the tests and docs ([#2506](https://github.com/TraceMachina/nativelink/issues/2506)) - ([d624f6a](https://github.com/TraceMachina/nativelink/commit/d624f6a483a7b5530506c3ca76d8890bae269239)) +- [web] restore blog posts deleted by the site redesign ([#2501](https://github.com/TraceMachina/nativelink/issues/2501)) - ([6dca947](https://github.com/TraceMachina/nativelink/commit/6dca947a135bea52585ebaa4a627510656e02b62)) +- Update LRE/store docs to fix #2406 ([#2498](https://github.com/TraceMachina/nativelink/issues/2498)) - ([5516d8b](https://github.com/TraceMachina/nativelink/commit/5516d8be1db4045e1d97bede230beab626f0f4e7)) +- Make cleanup wait timeout configurable in LocalWorkerConfig ([#2456](https://github.com/TraceMachina/nativelink/issues/2456)) - ([f25f68d](https://github.com/TraceMachina/nativelink/commit/f25f68d30eab57796a6bf03ef53f28fe04345384)) +- Improve other build systems docs ([#2448](https://github.com/TraceMachina/nativelink/issues/2448)) - ([bc1a294](https://github.com/TraceMachina/nativelink/commit/bc1a29413820c0b402d6f12a1ccfcc66601d3279)) + +### 🧪 Testing & CI + +- Fix custom image building ([#2477](https://github.com/TraceMachina/nativelink/issues/2477)) - ([452a720](https://github.com/TraceMachina/nativelink/commit/452a72035630a08bdd350c2584f210fe8f1cf476)) +- skip writes larger than max_bytes instead of buffering them ([#2473](https://github.com/TraceMachina/nativelink/issues/2473)) - ([1a10e3f](https://github.com/TraceMachina/nativelink/commit/1a10e3fa90c4dd769a3802b7e386b3a0c94fab98)) +- Fix various OCI image upload issues ([#2457](https://github.com/TraceMachina/nativelink/issues/2457)) - ([5f6be36](https://github.com/TraceMachina/nativelink/commit/5f6be36843380e61f6794587557bc5ff25fa3cbc)) +- Improve security scorecard ([#2452](https://github.com/TraceMachina/nativelink/issues/2452)) - ([babdfd9](https://github.com/TraceMachina/nativelink/commit/babdfd9412c45f2018de67f30b0811330c0f2874)) + +### ⚙️ Miscellaneous + +- *(release)* build aarch64 Linux binary on a native arm64 runner ([#2483](https://github.com/TraceMachina/nativelink/issues/2483)) - ([d55e0e5](https://github.com/TraceMachina/nativelink/commit/d55e0e5dc6a4b81e35afc9aa3886f412e28a8702)) +- *(release)* pin cosign to v2.5.3 for legacy .sig/.pem output ([#2480](https://github.com/TraceMachina/nativelink/issues/2480)) - ([1829fa4](https://github.com/TraceMachina/nativelink/commit/1829fa462e6e766b7ac1e5faf74e862925359d8d)) +- *(release)* keep legacy cosign signature/cert outputs ([#2478](https://github.com/TraceMachina/nativelink/issues/2478)) - ([2cd0507](https://github.com/TraceMachina/nativelink/commit/2cd0507b85efd6663bb574f49654e489a9175ea7)) +- *(release)* add signed release artifacts with SLSA provenance ([#2470](https://github.com/TraceMachina/nativelink/issues/2470)) - ([c72bce8](https://github.com/TraceMachina/nativelink/commit/c72bce8533913c70d121c0798f4c6d07ce78537a)) +- Migrate Azure Blob store to use the Azure v1.0 crates ([#2472](https://github.com/TraceMachina/nativelink/issues/2472)) - ([884ffde](https://github.com/TraceMachina/nativelink/commit/884ffdea523ad4ecb00e01c94a7d73cd86edf09a)) +- Remove reclient in favor of Siso ([#2510](https://github.com/TraceMachina/nativelink/issues/2510)) - ([d9325f8](https://github.com/TraceMachina/nativelink/commit/d9325f8cd3d80bb5f22bce03eda5ba3d83693dc6)) +- Strip stray item from changelog ([#2507](https://github.com/TraceMachina/nativelink/issues/2507)) - ([87d16c6](https://github.com/TraceMachina/nativelink/commit/87d16c6a33c4a75add5db499d8a2aa4a84750de1)) +- evict .exec variant when its digest is evicted ([#2474](https://github.com/TraceMachina/nativelink/issues/2474)) ([#2503](https://github.com/TraceMachina/nativelink/issues/2503)) - ([6a162e3](https://github.com/TraceMachina/nativelink/commit/6a162e37cf6315ad76638114129a31521e29356a)) +- Default --fallback for nix rather than having to set it everywhere ([#2475](https://github.com/TraceMachina/nativelink/issues/2475)) - ([f170cdf](https://github.com/TraceMachina/nativelink/commit/f170cdf41326ae9b24e5c0d1b2b344fdd165b4b6)) +- Rename time-based config values to have appropriate postfixes ([#2462](https://github.com/TraceMachina/nativelink/issues/2462)) - ([ccc01eb](https://github.com/TraceMachina/nativelink/commit/ccc01eb6c82584b0564a373d2405df30d389f316)) +- Disable various things we don't want renovate to upgrade ([#2469](https://github.com/TraceMachina/nativelink/issues/2469)) - ([414246b](https://github.com/TraceMachina/nativelink/commit/414246b160c8facf629797b9a95cac4e5a74d8d7)) +- == not != for multi-arch upload ([#2455](https://github.com/TraceMachina/nativelink/issues/2455)) - ([d74ba31](https://github.com/TraceMachina/nativelink/commit/d74ba31dd7a4a6e2c4d50f5594cb558f54f9b077)) +- Multi arch image publish with date-versioning ([#2454](https://github.com/TraceMachina/nativelink/issues/2454)) - ([65d43ed](https://github.com/TraceMachina/nativelink/commit/65d43ede9156caecb704ffb08eea9f6c93bb962f)) +- ref_store now resolves on boot, not first query ([#2451](https://github.com/TraceMachina/nativelink/issues/2451)) - ([0952bb6](https://github.com/TraceMachina/nativelink/commit/0952bb644202b05e82533bbe95eefd2841f4cffc)) + +### ⬆️ Bumps & Version Updates + +- *(web)* Replace hero visual with YouTube video embed ([#2443](https://github.com/TraceMachina/nativelink/issues/2443)) - ([d1863ca](https://github.com/TraceMachina/nativelink/commit/d1863ca3e34e6619b43ee2a55fd6dbf3093253aa)) +- Update dependency marked to v18 ([#2502](https://github.com/TraceMachina/nativelink/issues/2502)) - ([0d516b9](https://github.com/TraceMachina/nativelink/commit/0d516b903054f6fe320767a8c88203137c374c31)) +- Update Rust crate anyhow to v1.0.103 [SECURITY] ([#2494](https://github.com/TraceMachina/nativelink/issues/2494)) - ([d459bd9](https://github.com/TraceMachina/nativelink/commit/d459bd908b96f7cb09d971aeeb7406180a08de58)) +- Update Rust crate opentelemetry_sdk to 0.32.0 [SECURITY] ([#2487](https://github.com/TraceMachina/nativelink/issues/2487)) - ([ce3a919](https://github.com/TraceMachina/nativelink/commit/ce3a919ecce0e6f4ac3ebede5e90813b16f888f8)) +- Update website components ([#2486](https://github.com/TraceMachina/nativelink/issues/2486)) - ([3bfe408](https://github.com/TraceMachina/nativelink/commit/3bfe40897a02853635ba00107d6c193885799eea)) +- bump quinn-proto to 0.11.15 and memmap2 to 0.9.11 (RustSec) ([#2479](https://github.com/TraceMachina/nativelink/issues/2479)) - ([ae5762e](https://github.com/TraceMachina/nativelink/commit/ae5762e5412cede276c37e1f33f6f92788e68dfd)) +- Update dependency @types/node to v26 ([#2453](https://github.com/TraceMachina/nativelink/issues/2453)) - ([a9f5ce1](https://github.com/TraceMachina/nativelink/commit/a9f5ce104f6f0252806884d0608b7f34c27be0f4)) +- Update logo ([#2450](https://github.com/TraceMachina/nativelink/issues/2450)) - ([1989e8d](https://github.com/TraceMachina/nativelink/commit/1989e8d3b0ca274e7e3687c7edab3638c3f90849)) + ## [1.5.2](https://github.com/TraceMachina/nativelink/compare/v1.5.1..v1.5.2) - 2026-06-17 ### 🐛 Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index 3b35de285..85647be66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2953,7 +2953,7 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "nativelink" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "axum", @@ -2984,7 +2984,7 @@ dependencies = [ [[package]] name = "nativelink-config" -version = "1.5.2" +version = "1.6.0" dependencies = [ "byte-unit", "humantime", @@ -3003,7 +3003,7 @@ dependencies = [ [[package]] name = "nativelink-error" -version = "1.5.2" +version = "1.6.0" dependencies = [ "base64 0.22.1", "mongodb", @@ -3027,7 +3027,7 @@ dependencies = [ [[package]] name = "nativelink-macro" -version = "1.5.2" +version = "1.6.0" dependencies = [ "proc-macro2", "quote", @@ -3036,7 +3036,7 @@ dependencies = [ [[package]] name = "nativelink-metric" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "nativelink-metric-macro-derive", @@ -3047,7 +3047,7 @@ dependencies = [ [[package]] name = "nativelink-metric-macro-derive" -version = "1.5.2" +version = "1.6.0" dependencies = [ "proc-macro2", "quote", @@ -3056,7 +3056,7 @@ dependencies = [ [[package]] name = "nativelink-proto" -version = "1.5.2" +version = "1.6.0" dependencies = [ "derive_more 2.1.0", "prost", @@ -3068,7 +3068,7 @@ dependencies = [ [[package]] name = "nativelink-redis-tester" -version = "1.5.2" +version = "1.6.0" dependencies = [ "either", "nativelink-util", @@ -3081,7 +3081,7 @@ dependencies = [ [[package]] name = "nativelink-scheduler" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "async-trait", @@ -3118,7 +3118,7 @@ dependencies = [ [[package]] name = "nativelink-service" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "async-trait", @@ -3159,7 +3159,7 @@ dependencies = [ [[package]] name = "nativelink-store" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "async-trait", @@ -3237,7 +3237,7 @@ dependencies = [ [[package]] name = "nativelink-util" -version = "1.5.2" +version = "1.6.0" dependencies = [ "anyhow", "async-trait", @@ -3299,7 +3299,7 @@ dependencies = [ [[package]] name = "nativelink-worker" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "bytes", diff --git a/Cargo.toml b/Cargo.toml index eeba7a5dc..cf3e336fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ resolver = "2" edition = "2024" name = "nativelink" rust-version = "1.93.1" -version = "1.5.2" +version = "1.6.0" [profile.release] lto = true diff --git a/MODULE.bazel b/MODULE.bazel index eae78d033..1fff5e30e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "nativelink", - version = "1.5.2", + version = "1.6.0", compatibility_level = 0, ) diff --git a/nativelink-config/Cargo.toml b/nativelink-config/Cargo.toml index 0b2474206..cdaea071c 100644 --- a/nativelink-config/Cargo.toml +++ b/nativelink-config/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-config" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-error = { path = "../nativelink-error" } diff --git a/nativelink-error/Cargo.toml b/nativelink-error/Cargo.toml index 2fde8ec45..80ee221e3 100644 --- a/nativelink-error/Cargo.toml +++ b/nativelink-error/Cargo.toml @@ -8,7 +8,7 @@ autoexamples = false autotests = true edition = "2024" name = "nativelink-error" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-metric = { path = "../nativelink-metric" } diff --git a/nativelink-macro/Cargo.toml b/nativelink-macro/Cargo.toml index 5b9bb8937..9c211626d 100644 --- a/nativelink-macro/Cargo.toml +++ b/nativelink-macro/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-macro" -version = "1.5.2" +version = "1.6.0" [lib] proc-macro = true diff --git a/nativelink-metric/Cargo.toml b/nativelink-metric/Cargo.toml index 15162b7da..30752871b 100644 --- a/nativelink-metric/Cargo.toml +++ b/nativelink-metric/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-metric" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-metric-macro-derive = { path = "nativelink-metric-macro-derive" } diff --git a/nativelink-metric/nativelink-metric-macro-derive/Cargo.toml b/nativelink-metric/nativelink-metric-macro-derive/Cargo.toml index 37f45137e..ba7666cf7 100644 --- a/nativelink-metric/nativelink-metric-macro-derive/Cargo.toml +++ b/nativelink-metric/nativelink-metric-macro-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2024" name = "nativelink-metric-macro-derive" -version = "1.5.2" +version = "1.6.0" [lib] proc-macro = true diff --git a/nativelink-proto/Cargo.toml b/nativelink-proto/Cargo.toml index 599a896f2..58d202aaa 100644 --- a/nativelink-proto/Cargo.toml +++ b/nativelink-proto/Cargo.toml @@ -2,7 +2,7 @@ [package] edition = "2024" name = "nativelink-proto" -version = "1.5.2" +version = "1.6.0" [lib] doctest = false # because some of the generated protos have things that look like doctests but break diff --git a/nativelink-redis-tester/Cargo.toml b/nativelink-redis-tester/Cargo.toml index cbb65e9ea..2cd9c7dd3 100644 --- a/nativelink-redis-tester/Cargo.toml +++ b/nativelink-redis-tester/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-redis-tester" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-util = { path = "../nativelink-util" } diff --git a/nativelink-scheduler/Cargo.toml b/nativelink-scheduler/Cargo.toml index 392f5d413..2117c16a3 100644 --- a/nativelink-scheduler/Cargo.toml +++ b/nativelink-scheduler/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-scheduler" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-config = { path = "../nativelink-config" } diff --git a/nativelink-service/Cargo.toml b/nativelink-service/Cargo.toml index b830a129e..e7369f8f8 100644 --- a/nativelink-service/Cargo.toml +++ b/nativelink-service/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-service" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-config = { path = "../nativelink-config" } diff --git a/nativelink-store/Cargo.toml b/nativelink-store/Cargo.toml index 586bc6701..add97b227 100644 --- a/nativelink-store/Cargo.toml +++ b/nativelink-store/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-store" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-config = { path = "../nativelink-config" } diff --git a/nativelink-test/fuzz/Cargo.lock b/nativelink-test/fuzz/Cargo.lock index c5469f6bf..58f5ba621 100644 --- a/nativelink-test/fuzz/Cargo.lock +++ b/nativelink-test/fuzz/Cargo.lock @@ -1038,7 +1038,7 @@ dependencies = [ [[package]] name = "nativelink-config" -version = "1.5.2" +version = "1.6.0" dependencies = [ "byte-unit", "humantime", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "nativelink-error" -version = "1.5.2" +version = "1.6.0" dependencies = [ "base64", "mongodb", @@ -1085,7 +1085,7 @@ dependencies = [ [[package]] name = "nativelink-metric" -version = "1.5.2" +version = "1.6.0" dependencies = [ "async-lock", "nativelink-metric-macro-derive", @@ -1096,7 +1096,7 @@ dependencies = [ [[package]] name = "nativelink-metric-macro-derive" -version = "1.5.2" +version = "1.6.0" dependencies = [ "proc-macro2", "quote", @@ -1105,7 +1105,7 @@ dependencies = [ [[package]] name = "nativelink-proto" -version = "1.5.2" +version = "1.6.0" dependencies = [ "derive_more", "prost", diff --git a/nativelink-util/Cargo.toml b/nativelink-util/Cargo.toml index 79b1f88de..8506a3201 100644 --- a/nativelink-util/Cargo.toml +++ b/nativelink-util/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-util" -version = "1.5.2" +version = "1.6.0" [dependencies] nativelink-config = { path = "../nativelink-config" } diff --git a/nativelink-worker/Cargo.toml b/nativelink-worker/Cargo.toml index 107bb0603..05aaaa225 100644 --- a/nativelink-worker/Cargo.toml +++ b/nativelink-worker/Cargo.toml @@ -4,7 +4,7 @@ lints.workspace = true [package] edition = "2024" name = "nativelink-worker" -version = "1.5.2" +version = "1.6.0" [features] nix = [] From 1f9486699a2699d017fa6452619ab79e432702f5 Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Mon, 6 Jul 2026 16:30:36 -0700 Subject: [PATCH 024/144] Upgrade to Bazel 9.1.1 and 8.7.0 (#2514) --- .bazelversion | 2 +- .github/workflows/native-bazel.yaml | 32 ++++++++++++++++++++++++----- MODULE.bazel.lock | 10 ++++----- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/.bazelversion b/.bazelversion index 3beeadd42..44931da26 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -9.0.2 +9.1.1 diff --git a/.github/workflows/native-bazel.yaml b/.github/workflows/native-bazel.yaml index f6442c6e0..fc8270452 100644 --- a/.github/workflows/native-bazel.yaml +++ b/.github/workflows/native-bazel.yaml @@ -24,10 +24,29 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, macos-26] - name: ${{ matrix.os }} + include: + # Keep the existing required check names on the project-pinned Bazel. + - os: ubuntu-24.04 + bazel_version: 9.1.1 + lockfile_mode: error + check_name: ubuntu-24.04 + - os: macos-26 + bazel_version: 9.1.1 + lockfile_mode: error + check_name: macos-26 + # Deterministic Bazel compatibility lane. Randomizing Bazel versions + # makes failures harder to reproduce. Bazel 8 cannot read the + # Bazel 9 lockfile format, so this lane tests source compatibility + # without enforcing the committed MODULE.bazel.lock. + - os: ubuntu-24.04 + bazel_version: 8.7.0 + lockfile_mode: "off" + check_name: ubuntu-24.04 / Bazel 8.7.0 + name: ${{ matrix.check_name }} runs-on: ${{ matrix.os }} timeout-minutes: 30 + env: + USE_BAZEL_VERSION: ${{ matrix.bazel_version }} steps: - name: Checkout uses: >- # v6.0.2 @@ -42,18 +61,21 @@ jobs: with: bazelisk-cache: true repository-cache: true - disk-cache: ${{ github.workflow }}-${{ matrix.os }} + disk-cache: ${{ github.workflow }}-${{ matrix.os }}-bazel-${{ matrix.bazel_version }} + + - name: Show Bazel version + run: bazel --version - name: Run Bazel tests run: | if [ "$RUNNER_OS" == "Linux" ] || [ "$RUNNER_OS" == "macOS" ]; then bazel test //... \ - --lockfile_mode=error \ + --lockfile_mode=${{ matrix.lockfile_mode }} \ --extra_toolchains=@rust_toolchains//:all \ --verbose_failures elif [ "$RUNNER_OS" == "Windows" ]; then bazel \ - --lockfile_mode=error \ + --lockfile_mode=${{ matrix.lockfile_mode }} \ --output_user_root=${{ steps.bazel-cache.outputs.mountpoint }} \ test \ --config=windows \ diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 5f75317fe..52ac7b662 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -155,8 +155,8 @@ "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_java/8.6.0/MODULE.bazel": "9c064c434606d75a086f15ade5edb514308cccd1544c2b2a89bbac4310e41c71", "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", - "https://bcr.bazel.build/modules/rules_java/9.0.3/MODULE.bazel": "1f98ed015f7e744a745e0df6e898a7c5e83562d6b759dfd475c76456dda5ccea", - "https://bcr.bazel.build/modules/rules_java/9.0.3/source.json": "b038c0c07e12e658135bbc32cc1a2ded6e33785105c9d41958014c592de4593e", + "https://bcr.bazel.build/modules/rules_java/9.1.0/MODULE.bazel": "ee63f27e36a3fada80342869361182f120a9819c74320e8e65b1e04ba0cd7a9d", + "https://bcr.bazel.build/modules/rules_java/9.1.0/source.json": "da589573c1dee2c9ac4a568b301269a2e8191110ff0345c1a959fa7ea6c4dfd6", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", @@ -328,7 +328,7 @@ }, "@@pybind11_bazel+//:internal_configure.bzl%internal_configure_extension": { "general": { - "bzlTransitiveDigest": "53kgvDiJoicCJNGFz6d2h61Tuh/fKfR+BEDbe+9/8SY=", + "bzlTransitiveDigest": "/zSuBEqJ9BGcQV0BWE0KXHwECZB0L0FQ7oGDb2maycA=", "usagesDigest": "D1r3lfzMuUBFxgG8V6o0bQTLMk3GkaGOaPzw53wrwyw=", "recordedInputs": [ "REPO_MAPPING:pybind11_bazel+,bazel_tools bazel_tools", @@ -350,7 +350,7 @@ }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "ACFsXqVGkz5ZZ1Xi44Dbv+DpY+0T3E98jzGNsvDSONs=", + "bzlTransitiveDigest": "76FMguAa6TZAMN5inJ4/C9SZ0VlZnKOlVnlRe9bgvd8=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedInputs": [ "REPO_MAPPING:rules_kotlin+,bazel_tools bazel_tools" @@ -437,7 +437,7 @@ }, "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { "general": { - "bzlTransitiveDigest": "ZfstfSYBzT+mMfZUBGDjUgj967LyAo5dtVLFDSbphGY=", + "bzlTransitiveDigest": "A+ROJOpIeiQkfLxK27VU3L2WCGCdsciDVdpEFctkd1w=", "usagesDigest": "w6DeRbiDSXRVPZPJF6BTEEe4fMh1OiL8grDVrOA0M98=", "recordedInputs": [ "REPO_MAPPING:bazel_features+,bazel_features_globals bazel_features++version_extension+bazel_features_globals", From 05c5fac5efdf843b8962bae79a8457c51eb62bd7 Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Tue, 7 Jul 2026 05:59:24 +0530 Subject: [PATCH 025/144] Attach the tag to images pushed on a tag build (#2515) Co-authored-by: Marcus Eagan --- .github/actions/test-and-upload-image/action.yaml | 2 +- .github/workflows/tagged_image.yaml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/test-and-upload-image/action.yaml b/.github/actions/test-and-upload-image/action.yaml index 1bd1aecf8..3482dda9f 100644 --- a/.github/actions/test-and-upload-image/action.yaml +++ b/.github/actions/test-and-upload-image/action.yaml @@ -55,7 +55,7 @@ runs: - name: Upload image if: ${{ inputs.multi-arch == 'false' && (inputs.force-push == 'true' || contains(fromJson('["refs/heads/main"]'), github.ref)) }} run: | - nix run .#publish-ghcr ${{ inputs.image }} + nix run .#publish-ghcr ${{ inputs.image }} ${{ inputs.tag }} env: GHCR_REGISTRY: ghcr.io/${{ github.repository_owner }} GHCR_USERNAME: ${{ inputs.GHCR_USERNAME }} diff --git a/.github/workflows/tagged_image.yaml b/.github/workflows/tagged_image.yaml index 7ee1c7138..79822fd0c 100644 --- a/.github/workflows/tagged_image.yaml +++ b/.github/workflows/tagged_image.yaml @@ -53,6 +53,11 @@ jobs: multi-arch: ${{ matrix.multi-arch }} components: ${{ matrix.components }} tag: ${{github.ref_name}} + # This workflow only runs on tag pushes, so github.ref is + # refs/tags/* (never refs/heads/main). Without force-push the upload + # steps in test-and-upload-image are skipped and the tag never gets + # attached to the pushed images. + force-push: true GHCR_USERNAME: ${{ vars.GHCR_PUBLISH_USER }} GHCR_PASSWORD: ${{ secrets.GHCR_PUBLISH_TOKEN }} From 9d86aaf97a75c64b8a9c664ab0be0242f2c5a71b Mon Sep 17 00:00:00 2001 From: Ernesto Cambuston Date: Tue, 7 Jul 2026 05:39:15 -0700 Subject: [PATCH 026/144] Add REAPI content-defined chunking (SplitBlob/SpliceBlob) support (#2497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add REAPI content-defined chunking (SplitBlob/SpliceBlob) support Implements the server side of the remote-apis blob split/splice extension used by Bazel's --experimental_remote_cache_chunking (Bazel 8.7.0+/9.1.0+), fixes #2496. - Vendor SplitBlob/SpliceBlob RPCs, ChunkingFunction, FastCdc2020Params and CacheCapabilities fields 8-12 from upstream remote-apis. - SpliceBlob re-assembles chunked uploads: verifies chunk existence and the spliced digest before committing, materializes the blob so non-chunking clients stay correct, and persists the chunk layout in a configurable index store. Chunk reads are pipelined while hashing stays in chunk order. - SplitBlob serves stored layouts (validated against the blob size so corrupt or truncated index entries are never served), or chunks blobs on demand with FastCDC 2020 (fastcdc crate, normalization level 2) so outputs uploaded whole by remote execution workers also get chunked downloads. Unusable layouts fall back to re-chunking. - Capabilities advertise split/splice support and FastCDC 2020 parameters per instance, collected across all server blocks, gated behind the new opt-in experimental_chunking CAS service config (off by default; zero behavior change when unset). - Reject foot-gun configs at startup: index_store == cas_store (chunk layouts stored under blob digests would overwrite blob content) and chunking on grpc proxy stores (would download/re-upload entire blobs instead of forwarding RPCs). - Conformance-test the chunker against the official REAPI fastcdc2020_test_vectors.txt (offsets, lengths, sha256s and gear fingerprints, seeds 0 and 666, in-memory and streaming). - Add ChunkingMetrics (splice/split totals, hit/miss/on-demand rates, byte counters, digest verification failures). * Forward SplitBlob/SpliceBlob natively through grpc proxy stores For grpc-store-backed CAS instances the chunking RPCs are now forwarded verbatim to the backend (with instance-name rewriting and the store's usual retry handling) instead of being rejected at startup. This makes NativeLink relays transparent for content-defined chunking: one RPC in, one RPC out, the backend owns chunking and the layout index. - Add GrpcStore::split_blob/splice_blob following the existing find_missing_blobs/batch_*/get_tree forwarding pattern. - Shortcut to the proxy in the CAS handlers before any local chunking machinery is consulted, mirroring the other four CAS RPCs. - Make experimental_chunking.index_store optional: required for locally chunked instances, rejected for grpc-store instances where the backend owns the chunk layouts. The capabilities service still advertises split/splice + FastCDC params from the same config block, so relay operators set avg_chunk_size_bytes to match their backend. - Test forwarding against a fake CAS backend over a real gRPC round trip (verifies passthrough and instance-name rewriting) and the new constructor rules. * Fix pre-commit hooks and make max chunk count configurable - Reuse the FastCDC test fixture already vendored at nativelink-util/tests/data/SekienAkashita.jpg (and already excluded from the forbid-binary-files hook) instead of adding a duplicate binary copy; export it from nativelink-util for the conformance test. - Format nativelink-service/Cargo.toml per taplo. - Replace the hardcoded 50k chunk cap with a per-instance experimental_chunking.max_chunk_count knob (default 50000). Blobs above the cap are served without chunking; the layout read cap is derived from the configured count so the two can never disagree. * Add chunking configuration example and clarify optionality Address review feedback: state explicitly that experimental_chunking is optional (with unchanged behavior when unset) and add a complete, test-validated configuration example at nativelink-config/examples/chunking_cas.json5. * Format chunking example per formatjson5 * Add chunking integration test to CI Now that the repo's Bazel is 9.1.1 (which supports --experimental_remote_cache_chunking), exercise the SplitBlob/SpliceBlob paths end-to-end in the existing integration-tests job: enable experimental_chunking in the docker-compose CAS config and add chunking_cache_test.sh, which uploads a ~6.9MB artifact as chunks, asserts the server registered a chunk layout, then re-fetches it through the chunked download path and verifies it is byte-identical. * Infer digest function when unset in chunking RPCs Bazel 9.1.1 leaves digest_function unset in SplitBlob/SpliceBlob even when running with --digest_function=blake3 (surfaced by the new CI chunking integration test, which runs with the repo's blake3 default). REAPI length-based inference cannot disambiguate SHA256 from BLAKE3 (both 32 bytes), so: - SpliceBlob hashes the re-assembled blob with both candidates when the field is unset and accepts whichever reproduces the expected digest. - SplitBlob's on-demand chunking infers the blob's digest function with an extra content pass so chunk digests use the right function. Explicitly-set digest functions keep the single-hasher fast path. * Fix artifact path resolution in chunking integration test The test runner's working directory is deployment-examples/docker-compose, not the workspace root, so the bazel-bin convenience symlink is not at the script's cwd. Resolve the output path through bazel info instead. * Add docs-site page for content-defined chunking Address review: document the feature where customers look — what it is, measured savings, client requirements (Bazel 9.1.1+/8.7+), how to enable it, tuning knobs, and when it does or does not help. * Disable async cache uploads in chunking integration test Bazel has uploaded cache entries in the background by default since Bazel 8 (--remote_cache_async), so `bazel build` can return before the chunked upload and SpliceBlob complete. The CI logs show exactly this: the splice reached the server ~0.9s after "Build completed successfully", while the test asserted on the chunk index ~0.8s after the build returned. Forcing synchronous uploads makes both the chunk-layout assertion and the clean-rebuild cache-hit check deterministic. Verified by running the full script locally against a clean server: splice on upload, layout written, remote cache hit via SplitBlob on rebuild, SHA-identical output. * Pass NATIVELINK_DIR through sudo to docker compose in test harness The integration test harness exports NATIVELINK_DIR but launches the containers with `sudo env RUST_LOG=info docker compose up`, and sudo's env_reset strips the variable. Docker compose therefore fell back to mounting root's ~/.cache/nativelink instead of the per-run cache dir, so the per-test `find "$NATIVELINK_DIR" -delete` cleanup never touched real store state, and chunking_cache_test.sh asserted on a directory the CAS container never wrote to (its chunk layouts landed in /root/.cache/nativelink on the host). Pass the variable through sudo explicitly, and add diagnostics to the chunking test's failure path that list both candidate locations. * Use absolute store paths in docker-compose CAS config NativeLink expands only environment variables in config strings (shellexpand::env — the tilde feature is not enabled), so the "~/.cache/nativelink/..." paths in local-storage-cas.json5 were treated as literal relative paths: inside the container the CAS wrote to "/~/.cache/nativelink" under the process cwd, an unmounted ephemeral directory, never to the /root/.cache/nativelink bind mount. worker.json5 already uses absolute paths, which is why executor data was host-visible while CAS data was not. Use absolute paths to match, and drop the same misleading tilde idiom from the docs-site config snippet. --- BUILD.bazel | 9 + Cargo.lock | 13 + MODULE.bazel.lock | 1 + .../docker-compose/local-storage-cas.json5 | 29 +- integration_tests/chunking_cache_test.sh | 74 ++ nativelink-config/examples/chunking_cas.json5 | 105 +++ nativelink-config/src/cas_server.rs | 111 ++- .../execution/v2/remote_execution.proto | 356 ++++++++ .../build.bazel.remote.execution.v2.pb.rs | 669 +++++++++++++++ nativelink-service/BUILD.bazel | 7 + nativelink-service/Cargo.toml | 2 + nativelink-service/src/capabilities_server.rs | 36 +- nativelink-service/src/cas_server.rs | 810 +++++++++++++++++- nativelink-service/tests/cas_server_test.rs | 716 +++++++++++++++- .../tests/data/fastcdc2020_test_vectors.txt | 35 + .../tests/fastcdc_conformance_test.rs | 152 ++++ nativelink-store/src/grpc_store.rs | 61 +- nativelink-store/src/verify_store.rs | 10 +- nativelink-store/tests/grpc_store_test.rs | 150 +++- nativelink-util/BUILD.bazel | 3 + nativelink-util/src/digest_hasher.rs | 8 + run_integration_tests.sh | 5 +- src/bin/nativelink.rs | 25 +- .../content/docs/configuration/chunking.mdx | 110 +++ .../docs/content/docs/configuration/meta.json | 3 +- 25 files changed, 3464 insertions(+), 36 deletions(-) create mode 100755 integration_tests/chunking_cache_test.sh create mode 100644 nativelink-config/examples/chunking_cas.json5 create mode 100644 nativelink-service/tests/data/fastcdc2020_test_vectors.txt create mode 100644 nativelink-service/tests/fastcdc_conformance_test.rs create mode 100644 web/apps/docs/content/docs/configuration/chunking.mdx diff --git a/BUILD.bazel b/BUILD.bazel index f9a482dea..700ba5dd3 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -133,3 +133,12 @@ sh_test( timeout = "short", srcs = [":dummy_test_sh"], ) + +# Large deterministic output used by integration_tests/chunking_cache_test.sh +# to exercise --experimental_remote_cache_chunking (must exceed the chunking +# threshold of 4x the average chunk size). +genrule( + name = "chunking_test_artifact", + outs = ["chunking_test_artifact.txt"], + cmd = "seq 1 1000000 > \"$@\"", +) diff --git a/Cargo.lock b/Cargo.lock index 85647be66..9efaf70f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1618,6 +1618,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fastcdc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf51ceb43e96afbfe4dd5c6f6082af5dfd60e220820b8123792d61963f2ce6bc" +dependencies = [ + "async-stream", + "tokio", + "tokio-stream", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3124,6 +3135,7 @@ dependencies = [ "async-trait", "axum", "bytes", + "fastcdc", "futures", "hex", "http-body-util", @@ -3149,6 +3161,7 @@ dependencies = [ "sha2", "tokio", "tokio-stream", + "tokio-util", "tonic", "tonic-prost", "tower", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 52ac7b662..89d6e6a81 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -931,6 +931,7 @@ "errno_0.3.14": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"hermit\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Diagnostics_Debug\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"libc/std\"]}}", "event-listener-strategy_0.5.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"event-listener/loom\"],\"portable-atomic\":[\"event-listener/portable-atomic\"],\"std\":[\"event-listener/std\"]}}", "event-listener_5.4.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"concurrent-queue\",\"req\":\"^2.4.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1.2.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"critical-section\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"parking\",\"optional\":true,\"req\":\"^2.0.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"portable_atomic_crate\",\"optional\":true,\"package\":\"portable-atomic\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"try-lock\",\"req\":\"^0.2.5\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"concurrent-queue/loom\",\"parking?/loom\",\"dep:loom\"],\"portable-atomic\":[\"portable-atomic-util\",\"portable_atomic_crate\",\"concurrent-queue/portable-atomic\"],\"std\":[\"concurrent-queue/std\",\"parking\"]}}", + "fastcdc_3.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"aes\",\"req\":\"^0.8.2\"},{\"name\":\"async-stream\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"byteorder\",\"req\":\"^1.4.3\"},{\"features\":[\"cargo\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.2.1\"},{\"kind\":\"dev\",\"name\":\"ctr\",\"req\":\"^0.9.2\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"md-5\",\"req\":\"^0.10.5\"},{\"kind\":\"dev\",\"name\":\"memmap2\",\"req\":\"^0.9.5\"},{\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"fs\",\"io-util\",\"rt\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"}],\"features\":{\"default\":[],\"futures\":[\"dep:futures\"],\"tokio\":[\"dep:tokio\",\"tokio-stream\",\"async-stream\"]}}", "fastrand_2.3.0": "{\"dependencies\":[{\"features\":[\"js\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wyhash\",\"req\":\"^0.5\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"js\":[\"std\",\"getrandom\"],\"std\":[\"alloc\"]}}", "fastrand_2.4.1": "{\"dependencies\":[{\"features\":[\"wasm_js\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.4\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3.4\"},{\"features\":[\"wasm_js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3.4\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wyhash\",\"req\":\"^0.6\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"js\":[\"std\",\"getrandom\"],\"std\":[\"alloc\"]}}", "ff_0.13.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitvec\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"blake2b_simd\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"byteorder\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"ff_derive\",\"optional\":true,\"req\":\"^0.13.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"subtle\",\"req\":\"^2.2.1\"}],\"features\":{\"alloc\":[],\"bits\":[\"bitvec\"],\"default\":[\"bits\",\"std\"],\"derive\":[\"byteorder\",\"ff_derive\"],\"derive_bits\":[\"bits\",\"ff_derive/bits\"],\"std\":[\"alloc\"]}}", diff --git a/deployment-examples/docker-compose/local-storage-cas.json5 b/deployment-examples/docker-compose/local-storage-cas.json5 index 6d4acdeed..69aae0134 100644 --- a/deployment-examples/docker-compose/local-storage-cas.json5 +++ b/deployment-examples/docker-compose/local-storage-cas.json5 @@ -13,8 +13,8 @@ }, backend: { filesystem: { - content_path: "~/.cache/nativelink/content_path-cas", - temp_path: "~/.cache/nativelink/tmp_path-cas", + content_path: "/root/.cache/nativelink/content_path-cas", + temp_path: "/root/.cache/nativelink/tmp_path-cas", eviction_policy: { // 10gb. max_bytes: 10000000000, @@ -23,11 +23,25 @@ }, }, }, + { + // Holds blob-to-chunks layouts for the SplitBlob/SpliceBlob RPCs used + // by Bazel's --experimental_remote_cache_chunking. Must not verify + // digests and must not be the same store as the CAS. + name: "CHUNK_INDEX_STORE", + filesystem: { + content_path: "/root/.cache/nativelink/content_path-chunk-index", + temp_path: "/root/.cache/nativelink/tmp_path-chunk-index", + eviction_policy: { + // 100mb. + max_bytes: 100000000, + }, + }, + }, { name: "AC_MAIN_STORE", filesystem: { - content_path: "~/.cache/nativelink/content_path-ac", - temp_path: "~/.cache/nativelink/tmp_path-ac", + content_path: "/root/.cache/nativelink/content_path-ac", + temp_path: "/root/.cache/nativelink/tmp_path-ac", eviction_policy: { // 500mb. max_bytes: 500000000, @@ -46,6 +60,13 @@ cas: [ { cas_store: "CAS_MAIN_STORE", + + // Optional: enables content-defined chunking + // (SplitBlob/SpliceBlob) for Bazel clients running with + // --experimental_remote_cache_chunking. + experimental_chunking: { + index_store: "CHUNK_INDEX_STORE", + }, }, ], ac: [ diff --git a/integration_tests/chunking_cache_test.sh b/integration_tests/chunking_cache_test.sh new file mode 100755 index 000000000..16351c1c1 --- /dev/null +++ b/integration_tests/chunking_cache_test.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Copyright 2026 The NativeLink Authors. All rights reserved. +# +# Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# See LICENSE file for details +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Sanity check for REAPI content-defined chunking: uploads a large blob via +# SpliceBlob (Bazel --experimental_remote_cache_chunking), verifies the +# server registered a chunk layout, then re-fetches the blob from the remote +# cache via the chunked download path and checks it is byte-identical. + +if [[ $UNDER_TEST_RUNNER -ne 1 ]]; then + echo "This script should be run under run_integration_tests.sh" + exit 1 +fi +set -x + +# Bazel uploads cache entries in the background by default +# (--remote_cache_async, on since Bazel 8), so a build can return before the +# chunked upload and SpliceBlob complete. Force synchronous uploads so the +# chunk-index assertions below cannot race the upload. +CHUNKING_FLAGS=(--config self_test --experimental_remote_cache_chunking --remote_cache_async=false) +EXPECTED_SHA=$(seq 1 1000000 | sha256sum | awk '{print $1}') +# The test runner's working directory is not the workspace root, so resolve +# the output location through bazel itself. +ARTIFACT="$(bazel --output_base="$BAZEL_CACHE_DIR" info "${CHUNKING_FLAGS[@]}" bazel-bin)/chunking_test_artifact.txt" + +# First build executes the action locally and uploads the ~6.9MB output as +# chunks (SpliceBlob). +bazel --output_base="$BAZEL_CACHE_DIR" build "${CHUNKING_FLAGS[@]}" //:chunking_test_artifact +FIRST_SHA=$(sha256sum "$ARTIFACT" | awk '{print $1}') +if [[ $FIRST_SHA != "$EXPECTED_SHA" ]]; then + echo "Expected locally built artifact to have sha $EXPECTED_SHA, got $FIRST_SHA." + exit 1 +fi + +# The server must have registered a chunk layout for the spliced blob. The +# index store is mounted from the host by docker-compose. +CHUNK_INDEX_DIR="${NATIVELINK_DIR:-$HOME/.cache/nativelink}/content_path-chunk-index" +if [[ -z $(find "$CHUNK_INDEX_DIR" -type f 2> /dev/null) ]]; then + echo "Expected a chunk layout in $CHUNK_INDEX_DIR after a chunked upload." + echo "SpliceBlob was likely not used; check that the server advertises" + echo "chunking support and that bazel supports the chunking flag." + echo "Diagnostics: contents of the mounted cache dir and root's default:" + sudo find "${NATIVELINK_DIR:-$HOME/.cache/nativelink}" -maxdepth 1 2> /dev/null || true + sudo find /root/.cache/nativelink -maxdepth 1 2> /dev/null || true + exit 1 +fi + +# Clean our local cache and re-fetch from the remote cache through the +# chunked download path. +bazel --output_base="$BAZEL_CACHE_DIR" clean +OUTPUT=$(bazel --output_base="$BAZEL_CACHE_DIR" build "${CHUNKING_FLAGS[@]}" //:chunking_test_artifact 2>&1) +if [[ ! $OUTPUT =~ 'remote cache hit' ]]; then + echo "Expected second bazel run to be a remote cache hit." + echo "STDOUT:" + echo "$OUTPUT" + exit 1 +fi +SECOND_SHA=$(sha256sum "$ARTIFACT" | awk '{print $1}') +if [[ $SECOND_SHA != "$EXPECTED_SHA" ]]; then + echo "Artifact fetched through the chunked download path is corrupt:" + echo "expected sha $EXPECTED_SHA, got $SECOND_SHA." + exit 1 +fi diff --git a/nativelink-config/examples/chunking_cas.json5 b/nativelink-config/examples/chunking_cas.json5 new file mode 100644 index 000000000..f2ca4b966 --- /dev/null +++ b/nativelink-config/examples/chunking_cas.json5 @@ -0,0 +1,105 @@ +// Demonstrates REAPI content-defined chunking: the SplitBlob/SpliceBlob +// RPCs used by Bazel's --experimental_remote_cache_chunking flag +// (available in Bazel 8.7.0+ / 9.1.0+). +// +// Chunking is entirely optional and disabled by default: without the +// `experimental_chunking` block below, NativeLink behaves exactly as +// before and does not advertise chunking support. When enabled, clients +// upload and download large blobs as content-defined chunks, so small +// changes to large outputs only transfer the chunks that changed. +{ + stores: [ + { + name: "CAS_MAIN_STORE", + filesystem: { + content_path: "/tmp/nativelink/data/content_path-cas", + temp_path: "/tmp/nativelink/data/tmp_path-cas", + eviction_policy: { + // 10gb. + max_bytes: 10000000000, + }, + }, + }, + { + // Holds the blob-to-chunks layouts registered via SpliceBlob or + // created by on-demand chunking in SplitBlob. Layout entries are + // small (roughly 80-140 bytes per chunk). This store must not verify + // content digests and must not be the same store as the CAS itself. + name: "CHUNK_INDEX_STORE", + filesystem: { + content_path: "/tmp/nativelink/data/content_path-chunk-index", + temp_path: "/tmp/nativelink/data/tmp_path-chunk-index", + eviction_policy: { + // 100mb. + max_bytes: 100000000, + }, + }, + }, + { + name: "AC_MAIN_STORE", + filesystem: { + content_path: "/tmp/nativelink/data/content_path-ac", + temp_path: "/tmp/nativelink/data/tmp_path-ac", + eviction_policy: { + // 500mb. + max_bytes: 500000000, + }, + }, + }, + ], + servers: [ + { + listener: { + http: { + socket_address: "0.0.0.0:50051", + }, + }, + services: { + cas: [ + { + instance_name: "main", + cas_store: "CAS_MAIN_STORE", + + // Optional: omit this block to disable chunking entirely. + experimental_chunking: { + // Required, unless `cas_store` is a grpc store — in that + // case the chunking RPCs are forwarded to the backend and + // `index_store` must be omitted. + index_store: "CHUNK_INDEX_STORE", + + // Optional: the average chunk size in bytes advertised to + // clients and used for server-side chunking. Must be between + // 1 KiB and 1 MiB. + // Default: 524288 (512 KiB). + avg_chunk_size_bytes: 524288, + + // Optional: blobs that would produce more chunks than this + // are served without chunking. + // Default: 50000. + max_chunk_count: 50000, + }, + }, + ], + ac: [ + { + instance_name: "main", + ac_store: "AC_MAIN_STORE", + }, + ], + + // The capabilities service advertises chunking support; Bazel only + // issues SplitBlob/SpliceBlob when it is advertised. + capabilities: [ + { + instance_name: "main", + }, + ], + bytestream: { + cas_stores: { + main: "CAS_MAIN_STORE", + }, + }, + }, + }, + ], +} diff --git a/nativelink-config/src/cas_server.rs b/nativelink-config/src/cas_server.rs index 5015e9aea..5f8b72984 100644 --- a/nativelink-config/src/cas_server.rs +++ b/nativelink-config/src/cas_server.rs @@ -121,7 +121,7 @@ pub struct AcStoreConfig { pub read_only: bool, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(deny_unknown_fields)] #[cfg_attr(feature = "dev-schema", derive(JsonSchema))] pub struct CasStoreConfig { @@ -129,6 +129,115 @@ pub struct CasStoreConfig { /// This store name referenced here may be reused multiple times. #[serde(deserialize_with = "convert_string_with_shellexpand")] pub cas_store: StoreRefName, + + /// Optional and experimental: enables the REAPI `SplitBlob`/`SpliceBlob` + /// RPCs used by content-defined chunking clients (e.g. Bazel's + /// `--experimental_remote_cache_chunking`). When set, the capabilities + /// service advertises blob split/splice support and `FastCDC` 2020 + /// parameters for this instance. When `cas_store` is a grpc store the + /// RPCs are forwarded to the backend (which must support chunking with + /// matching parameters); otherwise they are served locally. + /// + /// See `nativelink-config/examples/chunking_cas.json5` for a complete + /// configuration example. + /// + /// Default: not set — chunking RPCs are rejected, nothing is advertised, + /// and behavior is identical to when this option did not exist. + #[serde(default)] + pub experimental_chunking: Option, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +#[cfg_attr(feature = "dev-schema", derive(JsonSchema))] +pub struct CasChunkingConfig { + /// The store name referenced in the `stores` map in the main config used + /// to persist blob-to-chunks layouts. Keys are the digests of the + /// original blobs and values are serialized chunk layouts (which do not + /// hash to those digests), so this store MUST NOT perform content digest + /// verification and MUST NOT be the same store as `cas_store` — writing + /// layouts into the CAS would overwrite blob content. Using the same + /// store name as `cas_store` is rejected at startup. + /// + /// Required unless `cas_store` is a grpc store: for proxied instances + /// the `SplitBlob`/`SpliceBlob` RPCs are forwarded to the backend, which + /// owns the chunk layouts, and setting an `index_store` is rejected at + /// startup. + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub index_store: Option, + + /// The average chunk size in bytes advertised to clients through the + /// `FastCDC` 2020 capability parameters and used for server-side + /// chunking in `SplitBlob`. Clients derive the minimum and maximum + /// chunk sizes from this value (avg / 4 and avg * 4). The value must + /// be between 1 KiB and 1 MiB. + /// + /// Default: 524288 (512 KiB) + #[serde(default)] + pub avg_chunk_size_bytes: u64, + + /// Maximum number of chunks accepted in a `SpliceBlob` request or + /// produced by on-demand chunking in `SplitBlob`. Blobs that would + /// produce more chunks are served without chunking (`SplitBlob` returns + /// `NOT_FOUND` and clients fall back to a regular download). This bounds + /// the size of stored chunk layouts and of `SplitBlobResponse` messages + /// (roughly 80-140 bytes per chunk). At the default average chunk size + /// the default cap supports blobs up to ~25 GiB; note that values above + /// ~50000 may produce responses that exceed default gRPC message size + /// limits on clients. + /// + /// Default: 50000 + #[serde(default)] + pub max_chunk_count: u64, +} + +impl CasChunkingConfig { + /// Default for `avg_chunk_size_bytes`, the value recommended by the + /// REAPI spec for `FastCdc2020Params`. + pub const DEFAULT_AVG_CHUNK_SIZE_BYTES: u64 = 512 * 1024; + /// Bounds for `avg_chunk_size_bytes` mandated by the REAPI spec for + /// `FastCdc2020Params`. + pub const MIN_AVG_CHUNK_SIZE_BYTES: u64 = 1024; + pub const MAX_AVG_CHUNK_SIZE_BYTES: u64 = 1024 * 1024; + /// Default for `max_chunk_count`. + pub const DEFAULT_MAX_CHUNK_COUNT: u64 = 50_000; + + /// Returns `avg_chunk_size_bytes` with the default applied. + #[must_use] + pub const fn resolved_avg_chunk_size_bytes(&self) -> u64 { + if self.avg_chunk_size_bytes == 0 { + Self::DEFAULT_AVG_CHUNK_SIZE_BYTES + } else { + self.avg_chunk_size_bytes + } + } + + /// Returns `max_chunk_count` with the default applied. + #[must_use] + pub const fn resolved_max_chunk_count(&self) -> u64 { + if self.max_chunk_count == 0 { + Self::DEFAULT_MAX_CHUNK_COUNT + } else { + self.max_chunk_count + } + } + + /// Returns `avg_chunk_size_bytes` with the default applied, or an error + /// when the configured value is outside the REAPI-mandated bounds. + pub fn validated_avg_chunk_size_bytes(&self) -> Result { + let avg_chunk_size_bytes = self.resolved_avg_chunk_size_bytes(); + if !(Self::MIN_AVG_CHUNK_SIZE_BYTES..=Self::MAX_AVG_CHUNK_SIZE_BYTES) + .contains(&avg_chunk_size_bytes) + { + return Err(make_err!( + Code::InvalidArgument, + "'experimental_chunking.avg_chunk_size_bytes' is {avg_chunk_size_bytes}, must be between {} and {}", + Self::MIN_AVG_CHUNK_SIZE_BYTES, + Self::MAX_AVG_CHUNK_SIZE_BYTES + )); + } + Ok(avg_chunk_size_bytes) + } } #[derive(Deserialize, Serialize, Debug, Default)] diff --git a/nativelink-proto/build/bazel/remote/execution/v2/remote_execution.proto b/nativelink-proto/build/bazel/remote/execution/v2/remote_execution.proto index ebfcea3c7..b23d01ae0 100644 --- a/nativelink-proto/build/bazel/remote/execution/v2/remote_execution.proto +++ b/nativelink-proto/build/bazel/remote/execution/v2/remote_execution.proto @@ -429,6 +429,125 @@ service ContentAddressableStorage { rpc GetTree(GetTreeRequest) returns (stream GetTreeResponse) { option (google.api.http) = { get: "/v2/{instance_name=**}/blobs/{root_digest.hash}/{root_digest.size_bytes}:getTree" }; } + + // SplitBlob retrieves information about how a blob is split into chunks. + // + // This call returns information about how a blob is split into chunks, and + // returns a list of the chunk digests. Using the returned list of chunk digests, + // a client can check which chunks are locally available and only fetch the + // missing ones. The desired blob can be assembled by concatenating the fetched + // chunks in the order of the digests in the list. The chunks SHOULD all be + // available in the CAS. + // + // This API can be used to reduce the required data to download a large blob + // from CAS if some chunks from similar blobs are locally available. For this + // procedure to work properly, blobs SHOULD be split in a content-defined way, + // rather than with fixed-sized chunking. + // + // If a split request is answered successfully, a client can expect the + // following guarantees from the server: + // 1. The blob chunks are stored in CAS. + // 2. Concatenating the blob chunks in the order of the digest list returned + // by the server results in the original blob. + // + // Servers which implement this functionality MUST declare that they support + // it by setting the + // [CacheCapabilities.split_blob_support][build.bazel.remote.execution.v2.CacheCapabilities.split_blob_support] + // field accordingly. + // + // Clients MUST check that the server supports this capability, before using + // it. + // + // Clients SHOULD verify that the digest of the blob assembled by the fetched + // chunks is equal to the requested blob digest. + // + // The lifetimes of the generated chunk blobs MAY be independent of the + // lifetime of the original blob. In particular: + // * A blob and any chunk derived from it MAY be evicted from the CAS at + // different times. + // * A call to [SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + // extends the lifetime of the original blob, and sets the lifetimes of + // the resulting chunks (or extends the lifetimes of already-existing + // chunks). + // * Touching a chunk extends its lifetime, but the server MAY choose not + // to extend the lifetime of the original blob. + // * Touching the original blob extends its lifetime, but the server MAY + // choose not to extend the lifetimes of chunks derived from it. + // + // When blob splitting and splicing is used at the same time, the clients and + // the server SHOULD agree out-of-band upon a chunking algorithm used by both + // parties to benefit from each other's chunk data and avoid unnecessary data + // duplication. + // + // Errors: + // + // * `NOT_FOUND`: The requested blob is not present in the CAS, OR there is no + // split information available for the blob, OR at least one chunk needed to + // reconstruct the blob is missing from the CAS. + // * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the blob + // chunks. + rpc SplitBlob(SplitBlobRequest) returns (SplitBlobResponse) { + option (google.api.http) = { get: "/v2/{instance_name=**}/blobs/{blob_digest.hash}/{blob_digest.size_bytes}:splitBlob" }; + } + + // SpliceBlob tells the CAS how chunks can compose a blob. + // + // This is the complementary operation to the + // [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + // function to handle the chunked upload of large blobs to save upload + // traffic. + // + // When uploading a large blob using chunked upload, clients MUST first upload + // all chunks to the CAS, then call this RPC to tell the server how those chunks + // compose the original blob. The chunks referenced in the SpliceBlob call SHOULD be + // available in the CAS before calling this RPC. + // + // If a client needs to upload a large blob and is able to split a blob into + // chunks in such a way that reusable chunks are obtained, e.g., by means of + // content-defined chunking, it can first determine which parts of the blob + // are already available in the remote CAS and upload the missing chunks, and + // then use this API to store information on how the chunks compose the + // original blob. + // + // Servers which implement this functionality MUST declare that they support + // it by setting the + // [CacheCapabilities.splice_blob_support][build.bazel.remote.execution.v2.CacheCapabilities.splice_blob_support] + // field accordingly. + // + // Clients MUST check that the server supports this capability, before using + // it. + // + // In order to ensure data consistency of the CAS, the server MUST only add + // blobs to the CAS after verifying their digests. In particular, servers MUST NOT + // trust digests provided by the client. The server MAY accept a request as no-op + // if the client-specified blob is already in CAS or if information on how to + // construct the blob from chunks is available. If the client-specified blob is + // not already in the CAS, the server MUST verify that the digest of the newly + // created blob assembled from chunks matches the digest specified by the + // client, and reject the request if they differ. Servers MAY choose to allow + // overwriting existing chunk mappings or to store multiple chunk mappings for + // the same blob. + // + // When blob splitting and splicing is used at the same time, the clients and + // the server SHOULD agree out-of-band upon a chunking algorithm used by both + // parties to benefit from each other's chunk data and avoid unnecessary data + // duplication. + // + // Errors: + // + // * `NOT_FOUND`: At least one of the blob chunks is not present in the CAS. + // * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the + // spliced blob. + // * `INVALID_ARGUMENT`: The digest of the spliced blob is different from the + // provided expected digest. + // * `ALREADY_EXISTS`: The blob already exists in CAS and the server did not + // extend the lifetime of the chunks specified in the request, e.g. because + // it prefers a different chunking and extended those instead. Clients can + // call [SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + // to check what chunk mapping the server is using. + rpc SpliceBlob(SpliceBlobRequest) returns (SpliceBlobResponse) { + option (google.api.http) = { post: "/v2/{instance_name=**}/blobs:spliceBlob" body: "*" }; + } } // The Capabilities service may be used by remote execution clients to query @@ -1777,6 +1896,100 @@ message GetTreeResponse { string next_page_token = 2; } +// A request message for +// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob]. +message SplitBlobRequest { + // The instance of the execution system to operate against. A server may + // support multiple instances of the execution system (with their own workers, + // storage, caches, etc.). The server MAY require use of this field to select + // between them in an implementation-defined fashion, otherwise it can be + // omitted. + string instance_name = 1; + + // The digest of the blob to be split. + Digest blob_digest = 2; + + // The digest function of the blob to be split. + // + // If the digest function used is one of MD5, MURMUR3, SHA1, SHA256, + // SHA384, SHA512, or VSO, the client MAY leave this field unset. In + // that case the server SHOULD infer the digest function using the + // length of the blob digest hashes and the digest functions announced + // in the server's capabilities. + DigestFunction.Value digest_function = 3; + + // The chunking function that the client prefers to use. + // + // The server MAY use a different chunking function. + ChunkingFunction.Value chunking_function = 4; +} + +// A response message for +// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob]. +message SplitBlobResponse { + // The ordered list of digests of the chunks into which the blob was split. + // The original blob is assembled by concatenating the chunk data according to + // the order of the digests given by this list. + // + // The server MUST use the same digest function as the one explicitly or + // implicitly (through hash length) specified in the split request. + repeated Digest chunk_digests = 1; + + // The chunking function used to split the blob. + ChunkingFunction.Value chunking_function = 2; +} + +// A request message for +// [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob]. +message SpliceBlobRequest { + // The instance of the execution system to operate against. A server may + // support multiple instances of the execution system (with their own workers, + // storage, caches, etc.). The server MAY require use of this field to select + // between them in an implementation-defined fashion, otherwise it can be + // omitted. + string instance_name = 1; + + // Expected digest of the spliced blob. The client MUST set this field due + // to the following reasons: + // 1. It allows the server to perform an early existence check of the blob + // or existing chunks that assemble the blob before spending the splicing + // effort, as described in the [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob] + // documentation. + // 2. It allows servers with different storage backends to dispatch the + // request to the correct storage backend based on the size and/or the + // hash of the blob. + // 3. If chunking information already exists for the blob, it allows + // the server to keep the existing chunking information or replace it with + // new chunking information. + Digest blob_digest = 2; + + // The ordered list of digests of the chunks which need to be concatenated to + // assemble the original blob. + repeated Digest chunk_digests = 3; + + // The digest function of all chunks to be concatenated and of the blob to be + // spliced. The server MUST use the same digest function for both cases. + // + // If the digest function used is one of MD5, MURMUR3, SHA1, SHA256, SHA384, + // SHA512, or VSO, the client MAY leave this field unset. In that case the + // server SHOULD infer the digest function using the length of the blob digest + // hashes and the digest functions announced in the server's capabilities. + DigestFunction.Value digest_function = 4; + + // The chunking function that the client used to split the blob. + ChunkingFunction.Value chunking_function = 5; +} + +// A response message for +// [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob]. +message SpliceBlobResponse { + // Computed digest of the spliced blob. + // + // The server MUST use the same digest function as the one explicitly or + // implicitly (through hash length) specified in the splice request. + Digest blob_digest = 1; +} + // A request message for // [Capabilities.GetCapabilities][build.bazel.remote.execution.v2.Capabilities.GetCapabilities]. message GetCapabilitiesRequest { @@ -1967,6 +2180,34 @@ message Compressor { } } +// The chunking function is used to split a blob into chunks. +// +// The server advertises support for a chunking function by setting the +// corresponding params field in +// [CacheCapabilities][build.bazel.remote.execution.v2.CacheCapabilities]. +// For example, if fast_cdc_2020_params is set, the server supports FAST_CDC_2020. +// +// For optimal deduplication, clients SHOULD use an advertised chunking function. +// When clients use UNKNOWN, the server chooses an algorithm for SplitBlob and +// simply verifies chunk concatenation for SpliceBlob. +message ChunkingFunction { + enum Value { + // No specific algorithm. Servers MUST always accept this value. + // For SplitBlob, the server chooses the algorithm. For SpliceBlob, the + // server only verifies that chunks concatenate to form the expected blob. + UNKNOWN = 0; + + // The FastCDC chunking algorithm as described in the 2020 paper by + // Wen Xia, et al. See https://ieeexplore.ieee.org/document/9055082 + // for details. + FAST_CDC_2020 = 1; + + // The RepMaxCDC chunking algorithm as implemented by buildbarn/go-cdc. + // See https://github.com/buildbarn/go-cdc for details. + REP_MAX_CDC = 2; + } +} + // Capabilities of the remote cache system. message CacheCapabilities { // All the digest functions supported by the remote cache. @@ -2000,6 +2241,121 @@ message CacheCapabilities { // [BatchUpdateBlobs][build.bazel.remote.execution.v2.ContentAddressableStorage.BatchUpdateBlobs] // requests. repeated Compressor.Value supported_batch_update_compressors = 7; + + // The maximum blob size that the server will accept for CAS blob uploads. + // - If it is 0, it means there is no limit set. A client may assume + // arbitrarily large blobs may be uploaded to and downloaded from the cache. + // - If it is larger than 0, implementations SHOULD NOT attempt to upload + // blobs with size larger than the limit. Servers SHOULD reject blob + // uploads over the `max_cas_blob_size_bytes` limit with response code + // `INVALID_ARGUMENT` + // - If the cache implementation returns a given limit, it MAY still serve + // blobs larger than this limit. + int64 max_cas_blob_size_bytes = 8; + + // Whether blob splitting is supported for the particular server/instance. If + // yes, the server/instance implements the specified behavior for blob + // splitting and a meaningful result can be expected from the + // [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + // operation. + bool split_blob_support = 9; + + // Whether blob splicing is supported for the particular server/instance. If + // yes, the server/instance implements the specified behavior for blob + // splicing and a meaningful result can be expected from the + // [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob] + // operation. + bool splice_blob_support = 10; + + // The parameters for the FastCDC 2020 chunking algorithm. + // If set, the server supports the FastCDC chunking algorithm. + FastCdc2020Params fast_cdc_2020_params = 11; + + // The parameters for the RepMaxCDC chunking algorithm. + // If set, the server supports the RepMaxCDC chunking algorithm. + RepMaxCdcParams rep_max_cdc_params = 12; +} + +// Parameters for the FastCDC content-defined chunking algorithm. +// +// Implementations MUST follow the FastCDC 2020 paper by Wen Xia, et al.: +// https://ieeexplore.ieee.org/document/9055082 +// +// Supported implementations: +// - Rust: https://docs.rs/fastcdc/3.2.1/fastcdc/v2020/index.html +// - Go: https://github.com/buildbuddy-io/fastcdc2020 +// +// Test vectors can be found in the accompanying fastcdc2020_test_vectors.txt file. +// +// Implementations MUST use normalization level 2, which has been found +// successful for build artifacts with an average chunk size of 512 KiB. +// +// Key algorithm components from the paper: +// +// GEAR table: 256 64-bit integers for the rolling hash, computed as: +// GEAR[i] = high_64_bits(MD5(byte(i))) for i in 0..255 +// +// MASKS table: Bit patterns for chunk boundary detection, derived from +// the C reference implementation. The mask selection based on average +// chunk size SHOULD match the paper. +// +// The minimum and maximum chunk sizes MUST be derived from the average: +// - min_chunk_size = avg_chunk_size_bytes / 4 +// - max_chunk_size = avg_chunk_size_bytes * 4 +// +// Blobs smaller than max_chunk_size (avg_chunk_size_bytes * 4) SHOULD be +// uploaded without chunking. +// +// If any of the advertised parameters are not within the expected range, +// the client SHOULD ignore FastCDC chunking function support. +message FastCdc2020Params { + // The average (expected) chunk size for the FastCDC chunking algorithm. + // The value MUST be between 1 KiB and 1 MiB. The recommended value is + // 524288 (512 KiB). + uint64 avg_chunk_size_bytes = 1; + + // The seed for the FastCDC mask generation. + // The recommended value is 0. + // + // All clients sharing a cache SHOULD use the same seed to maximize + // chunk reuse. + uint32 seed = 2; +} + +// Parameters for the RepMaxCDC content-defined chunking algorithm. +// +// Supported implementations: +// - Go: https://github.com/buildbarn/go-cdc +// +// Key algorithm components: +// +// GEAR table: 256 64-bit integers for the rolling hash, computed as: +// GEAR[i] = high_64_bits(MD5(byte(i))) for i in 0..255 +// +// The algorithm repeatedly applies chunking until all chunks are in the +// range [min_chunk_size_bytes, 2*min_chunk_size_bytes). Cutting points are +// selected where the Gear rolling hash is maximized within a lookahead +// window of horizon_size_bytes. +// +// For sufficiently large files, the average chunk size prior to +// deduplication will approximately be min_chunk_size_bytes divided by +// Rényi's parking constant (0.7475979203...). More details: +// https://mathworld.wolfram.com/RenyisParkingConstants.html +// +// If any of the advertised parameters are not within the expected range, +// the client SHOULD ignore RepMaxCDC chunking function support. +message RepMaxCdcParams { + // The minimum chunk size for the RepMaxCDC chunking algorithm. + // The value MUST be at least 64 bytes (the Gear hash window size). + // All chunks will be in the range [min_chunk_size_bytes, 2*min_chunk_size_bytes). + // The recommended value is 262144 (256 KiB). + uint64 min_chunk_size_bytes = 1; + + // The lookahead window for finding optimal cutting points. + // Larger values improve deduplication quality with diminishing returns. + // Setting to 0 produces uniform chunks of min_chunk_size_bytes. + // The recommended value is 8 * min_chunk_size_bytes. + uint64 horizon_size_bytes = 2; } // Capabilities of the remote execution system. diff --git a/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs b/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs index b7c8f6634..f4b7c43e7 100644 --- a/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs +++ b/nativelink-proto/genproto/build.bazel.remote.execution.v2.pb.rs @@ -1438,6 +1438,104 @@ pub struct GetTreeResponse { pub next_page_token: ::prost::alloc::string::String, } /// A request message for +/// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob]. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SplitBlobRequest { + /// The instance of the execution system to operate against. A server may + /// support multiple instances of the execution system (with their own workers, + /// storage, caches, etc.). The server MAY require use of this field to select + /// between them in an implementation-defined fashion, otherwise it can be + /// omitted. + #[prost(string, tag = "1")] + pub instance_name: ::prost::alloc::string::String, + /// The digest of the blob to be split. + #[prost(message, optional, tag = "2")] + pub blob_digest: ::core::option::Option, + /// The digest function of the blob to be split. + /// + /// If the digest function used is one of MD5, MURMUR3, SHA1, SHA256, + /// SHA384, SHA512, or VSO, the client MAY leave this field unset. In + /// that case the server SHOULD infer the digest function using the + /// length of the blob digest hashes and the digest functions announced + /// in the server's capabilities. + #[prost(enumeration = "digest_function::Value", tag = "3")] + pub digest_function: i32, + /// The chunking function that the client prefers to use. + /// + /// The server MAY use a different chunking function. + #[prost(enumeration = "chunking_function::Value", tag = "4")] + pub chunking_function: i32, +} +/// A response message for +/// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob]. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SplitBlobResponse { + /// The ordered list of digests of the chunks into which the blob was split. + /// The original blob is assembled by concatenating the chunk data according to + /// the order of the digests given by this list. + /// + /// The server MUST use the same digest function as the one explicitly or + /// implicitly (through hash length) specified in the split request. + #[prost(message, repeated, tag = "1")] + pub chunk_digests: ::prost::alloc::vec::Vec, + /// The chunking function used to split the blob. + #[prost(enumeration = "chunking_function::Value", tag = "2")] + pub chunking_function: i32, +} +/// A request message for +/// [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob]. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SpliceBlobRequest { + /// The instance of the execution system to operate against. A server may + /// support multiple instances of the execution system (with their own workers, + /// storage, caches, etc.). The server MAY require use of this field to select + /// between them in an implementation-defined fashion, otherwise it can be + /// omitted. + #[prost(string, tag = "1")] + pub instance_name: ::prost::alloc::string::String, + /// Expected digest of the spliced blob. The client MUST set this field due + /// to the following reasons: + /// 1. It allows the server to perform an early existence check of the blob + /// or existing chunks that assemble the blob before spending the splicing + /// effort, as described in the [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob] + /// documentation. + /// 2. It allows servers with different storage backends to dispatch the + /// request to the correct storage backend based on the size and/or the + /// hash of the blob. + /// 3. If chunking information already exists for the blob, it allows + /// the server to keep the existing chunking information or replace it with + /// new chunking information. + #[prost(message, optional, tag = "2")] + pub blob_digest: ::core::option::Option, + /// The ordered list of digests of the chunks which need to be concatenated to + /// assemble the original blob. + #[prost(message, repeated, tag = "3")] + pub chunk_digests: ::prost::alloc::vec::Vec, + /// The digest function of all chunks to be concatenated and of the blob to be + /// spliced. The server MUST use the same digest function for both cases. + /// + /// If the digest function used is one of MD5, MURMUR3, SHA1, SHA256, SHA384, + /// SHA512, or VSO, the client MAY leave this field unset. In that case the + /// server SHOULD infer the digest function using the length of the blob digest + /// hashes and the digest functions announced in the server's capabilities. + #[prost(enumeration = "digest_function::Value", tag = "4")] + pub digest_function: i32, + /// The chunking function that the client used to split the blob. + #[prost(enumeration = "chunking_function::Value", tag = "5")] + pub chunking_function: i32, +} +/// A response message for +/// [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob]. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SpliceBlobResponse { + /// Computed digest of the spliced blob. + /// + /// The server MUST use the same digest function as the one explicitly or + /// implicitly (through hash length) specified in the splice request. + #[prost(message, optional, tag = "1")] + pub blob_digest: ::core::option::Option, +} +/// A request message for /// [Capabilities.GetCapabilities][build.bazel.remote.execution.v2.Capabilities.GetCapabilities]. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetCapabilitiesRequest { @@ -1749,6 +1847,68 @@ pub mod compressor { } } } +/// The chunking function is used to split a blob into chunks. +/// +/// The server advertises support for a chunking function by setting the +/// corresponding params field in +/// [CacheCapabilities][build.bazel.remote.execution.v2.CacheCapabilities]. +/// For example, if fast_cdc_2020_params is set, the server supports FAST_CDC_2020. +/// +/// For optimal deduplication, clients SHOULD use an advertised chunking function. +/// When clients use UNKNOWN, the server chooses an algorithm for SplitBlob and +/// simply verifies chunk concatenation for SpliceBlob. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ChunkingFunction {} +/// Nested message and enum types in `ChunkingFunction`. +pub mod chunking_function { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Value { + /// No specific algorithm. Servers MUST always accept this value. + /// For SplitBlob, the server chooses the algorithm. For SpliceBlob, the + /// server only verifies that chunks concatenate to form the expected blob. + Unknown = 0, + /// The FastCDC chunking algorithm as described in the 2020 paper by + /// Wen Xia, et al. See + /// for details. + FastCdc2020 = 1, + /// The RepMaxCDC chunking algorithm as implemented by buildbarn/go-cdc. + /// See for details. + RepMaxCdc = 2, + } + impl Value { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unknown => "UNKNOWN", + Self::FastCdc2020 => "FAST_CDC_2020", + Self::RepMaxCdc => "REP_MAX_CDC", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "UNKNOWN" => Some(Self::Unknown), + "FAST_CDC_2020" => Some(Self::FastCdc2020), + "REP_MAX_CDC" => Some(Self::RepMaxCdc), + _ => None, + } + } + } +} /// Capabilities of the remote cache system. #[derive(Clone, PartialEq, ::prost::Message)] pub struct CacheCapabilities { @@ -1786,6 +1946,123 @@ pub struct CacheCapabilities { /// requests. #[prost(enumeration = "compressor::Value", repeated, tag = "7")] pub supported_batch_update_compressors: ::prost::alloc::vec::Vec, + /// The maximum blob size that the server will accept for CAS blob uploads. + /// - If it is 0, it means there is no limit set. A client may assume + /// arbitrarily large blobs may be uploaded to and downloaded from the cache. + /// - If it is larger than 0, implementations SHOULD NOT attempt to upload + /// blobs with size larger than the limit. Servers SHOULD reject blob + /// uploads over the `max_cas_blob_size_bytes` limit with response code + /// `INVALID_ARGUMENT` + /// - If the cache implementation returns a given limit, it MAY still serve + /// blobs larger than this limit. + #[prost(int64, tag = "8")] + pub max_cas_blob_size_bytes: i64, + /// Whether blob splitting is supported for the particular server/instance. If + /// yes, the server/instance implements the specified behavior for blob + /// splitting and a meaningful result can be expected from the + /// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// operation. + #[prost(bool, tag = "9")] + pub split_blob_support: bool, + /// Whether blob splicing is supported for the particular server/instance. If + /// yes, the server/instance implements the specified behavior for blob + /// splicing and a meaningful result can be expected from the + /// [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob] + /// operation. + #[prost(bool, tag = "10")] + pub splice_blob_support: bool, + /// The parameters for the FastCDC 2020 chunking algorithm. + /// If set, the server supports the FastCDC chunking algorithm. + #[prost(message, optional, tag = "11")] + pub fast_cdc_2020_params: ::core::option::Option, + /// The parameters for the RepMaxCDC chunking algorithm. + /// If set, the server supports the RepMaxCDC chunking algorithm. + #[prost(message, optional, tag = "12")] + pub rep_max_cdc_params: ::core::option::Option, +} +/// Parameters for the FastCDC content-defined chunking algorithm. +/// +/// Implementations MUST follow the FastCDC 2020 paper by Wen Xia, et al.: +/// +/// +/// Supported implementations: +/// - Rust: +/// - Go: +/// +/// Test vectors can be found in the accompanying fastcdc2020_test_vectors.txt file. +/// +/// Implementations MUST use normalization level 2, which has been found +/// successful for build artifacts with an average chunk size of 512 KiB. +/// +/// Key algorithm components from the paper: +/// +/// GEAR table: 256 64-bit integers for the rolling hash, computed as: +/// GEAR\[i\] = high_64_bits(MD5(byte(i))) for i in 0..255 +/// +/// MASKS table: Bit patterns for chunk boundary detection, derived from +/// the C reference implementation. The mask selection based on average +/// chunk size SHOULD match the paper. +/// +/// The minimum and maximum chunk sizes MUST be derived from the average: +/// - min_chunk_size = avg_chunk_size_bytes / 4 +/// - max_chunk_size = avg_chunk_size_bytes * 4 +/// +/// Blobs smaller than max_chunk_size (avg_chunk_size_bytes * 4) SHOULD be +/// uploaded without chunking. +/// +/// If any of the advertised parameters are not within the expected range, +/// the client SHOULD ignore FastCDC chunking function support. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct FastCdc2020Params { + /// The average (expected) chunk size for the FastCDC chunking algorithm. + /// The value MUST be between 1 KiB and 1 MiB. The recommended value is + /// 524288 (512 KiB). + #[prost(uint64, tag = "1")] + pub avg_chunk_size_bytes: u64, + /// The seed for the FastCDC mask generation. + /// The recommended value is 0. + /// + /// All clients sharing a cache SHOULD use the same seed to maximize + /// chunk reuse. + #[prost(uint32, tag = "2")] + pub seed: u32, +} +/// Parameters for the RepMaxCDC content-defined chunking algorithm. +/// +/// Supported implementations: +/// - Go: +/// +/// Key algorithm components: +/// +/// GEAR table: 256 64-bit integers for the rolling hash, computed as: +/// GEAR\[i\] = high_64_bits(MD5(byte(i))) for i in 0..255 +/// +/// The algorithm repeatedly applies chunking until all chunks are in the +/// range [min_chunk_size_bytes, 2*min_chunk_size_bytes). Cutting points are +/// selected where the Gear rolling hash is maximized within a lookahead +/// window of horizon_size_bytes. +/// +/// For sufficiently large files, the average chunk size prior to +/// deduplication will approximately be min_chunk_size_bytes divided by +/// Rényi's parking constant (0.7475979203...). More details: +/// +/// +/// If any of the advertised parameters are not within the expected range, +/// the client SHOULD ignore RepMaxCDC chunking function support. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RepMaxCdcParams { + /// The minimum chunk size for the RepMaxCDC chunking algorithm. + /// The value MUST be at least 64 bytes (the Gear hash window size). + /// All chunks will be in the range [min_chunk_size_bytes, 2*min_chunk_size_bytes). + /// The recommended value is 262144 (256 KiB). + #[prost(uint64, tag = "1")] + pub min_chunk_size_bytes: u64, + /// The lookahead window for finding optimal cutting points. + /// Larger values improve deduplication quality with diminishing returns. + /// Setting to 0 produces uniform chunks of min_chunk_size_bytes. + /// The recommended value is 8 * min_chunk_size_bytes. + #[prost(uint64, tag = "2")] + pub horizon_size_bytes: u64, } /// Capabilities of the remote execution system. #[derive(Clone, PartialEq, ::prost::Message)] @@ -3340,6 +3617,175 @@ pub mod content_addressable_storage_client { ); self.inner.server_streaming(req, path, codec).await } + /// SplitBlob retrieves information about how a blob is split into chunks. + /// + /// This call returns information about how a blob is split into chunks, and + /// returns a list of the chunk digests. Using the returned list of chunk digests, + /// a client can check which chunks are locally available and only fetch the + /// missing ones. The desired blob can be assembled by concatenating the fetched + /// chunks in the order of the digests in the list. The chunks SHOULD all be + /// available in the CAS. + /// + /// This API can be used to reduce the required data to download a large blob + /// from CAS if some chunks from similar blobs are locally available. For this + /// procedure to work properly, blobs SHOULD be split in a content-defined way, + /// rather than with fixed-sized chunking. + /// + /// If a split request is answered successfully, a client can expect the + /// following guarantees from the server: + /// 1. The blob chunks are stored in CAS. + /// 2. Concatenating the blob chunks in the order of the digest list returned + /// by the server results in the original blob. + /// + /// Servers which implement this functionality MUST declare that they support + /// it by setting the + /// [CacheCapabilities.split_blob_support][build.bazel.remote.execution.v2.CacheCapabilities.split_blob_support] + /// field accordingly. + /// + /// Clients MUST check that the server supports this capability, before using + /// it. + /// + /// Clients SHOULD verify that the digest of the blob assembled by the fetched + /// chunks is equal to the requested blob digest. + /// + /// The lifetimes of the generated chunk blobs MAY be independent of the + /// lifetime of the original blob. In particular: + /// * A blob and any chunk derived from it MAY be evicted from the CAS at + /// different times. + /// * A call to [SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// extends the lifetime of the original blob, and sets the lifetimes of + /// the resulting chunks (or extends the lifetimes of already-existing + /// chunks). + /// * Touching a chunk extends its lifetime, but the server MAY choose not + /// to extend the lifetime of the original blob. + /// * Touching the original blob extends its lifetime, but the server MAY + /// choose not to extend the lifetimes of chunks derived from it. + /// + /// When blob splitting and splicing is used at the same time, the clients and + /// the server SHOULD agree out-of-band upon a chunking algorithm used by both + /// parties to benefit from each other's chunk data and avoid unnecessary data + /// duplication. + /// + /// Errors: + /// + /// * `NOT_FOUND`: The requested blob is not present in the CAS, OR there is no + /// split information available for the blob, OR at least one chunk needed to + /// reconstruct the blob is missing from the CAS. + /// * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the blob + /// chunks. + pub async fn split_blob( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ContentAddressableStorage/SplitBlob", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ContentAddressableStorage", + "SplitBlob", + ), + ); + self.inner.unary(req, path, codec).await + } + /// SpliceBlob tells the CAS how chunks can compose a blob. + /// + /// This is the complementary operation to the + /// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// function to handle the chunked upload of large blobs to save upload + /// traffic. + /// + /// When uploading a large blob using chunked upload, clients MUST first upload + /// all chunks to the CAS, then call this RPC to tell the server how those chunks + /// compose the original blob. The chunks referenced in the SpliceBlob call SHOULD be + /// available in the CAS before calling this RPC. + /// + /// If a client needs to upload a large blob and is able to split a blob into + /// chunks in such a way that reusable chunks are obtained, e.g., by means of + /// content-defined chunking, it can first determine which parts of the blob + /// are already available in the remote CAS and upload the missing chunks, and + /// then use this API to store information on how the chunks compose the + /// original blob. + /// + /// Servers which implement this functionality MUST declare that they support + /// it by setting the + /// [CacheCapabilities.splice_blob_support][build.bazel.remote.execution.v2.CacheCapabilities.splice_blob_support] + /// field accordingly. + /// + /// Clients MUST check that the server supports this capability, before using + /// it. + /// + /// In order to ensure data consistency of the CAS, the server MUST only add + /// blobs to the CAS after verifying their digests. In particular, servers MUST NOT + /// trust digests provided by the client. The server MAY accept a request as no-op + /// if the client-specified blob is already in CAS or if information on how to + /// construct the blob from chunks is available. If the client-specified blob is + /// not already in the CAS, the server MUST verify that the digest of the newly + /// created blob assembled from chunks matches the digest specified by the + /// client, and reject the request if they differ. Servers MAY choose to allow + /// overwriting existing chunk mappings or to store multiple chunk mappings for + /// the same blob. + /// + /// When blob splitting and splicing is used at the same time, the clients and + /// the server SHOULD agree out-of-band upon a chunking algorithm used by both + /// parties to benefit from each other's chunk data and avoid unnecessary data + /// duplication. + /// + /// Errors: + /// + /// * `NOT_FOUND`: At least one of the blob chunks is not present in the CAS. + /// * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the + /// spliced blob. + /// * `INVALID_ARGUMENT`: The digest of the spliced blob is different from the + /// provided expected digest. + /// * `ALREADY_EXISTS`: The blob already exists in CAS and the server did not + /// extend the lifetime of the chunks specified in the request, e.g. because + /// it prefers a different chunking and extended those instead. Clients can + /// call [SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// to check what chunk mapping the server is using. + pub async fn splice_blob( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/build.bazel.remote.execution.v2.ContentAddressableStorage/SpliceBlob", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "build.bazel.remote.execution.v2.ContentAddressableStorage", + "SpliceBlob", + ), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -3461,6 +3907,131 @@ pub mod content_addressable_storage_server { &self, request: tonic::Request, ) -> std::result::Result, tonic::Status>; + /// SplitBlob retrieves information about how a blob is split into chunks. + /// + /// This call returns information about how a blob is split into chunks, and + /// returns a list of the chunk digests. Using the returned list of chunk digests, + /// a client can check which chunks are locally available and only fetch the + /// missing ones. The desired blob can be assembled by concatenating the fetched + /// chunks in the order of the digests in the list. The chunks SHOULD all be + /// available in the CAS. + /// + /// This API can be used to reduce the required data to download a large blob + /// from CAS if some chunks from similar blobs are locally available. For this + /// procedure to work properly, blobs SHOULD be split in a content-defined way, + /// rather than with fixed-sized chunking. + /// + /// If a split request is answered successfully, a client can expect the + /// following guarantees from the server: + /// 1. The blob chunks are stored in CAS. + /// 2. Concatenating the blob chunks in the order of the digest list returned + /// by the server results in the original blob. + /// + /// Servers which implement this functionality MUST declare that they support + /// it by setting the + /// [CacheCapabilities.split_blob_support][build.bazel.remote.execution.v2.CacheCapabilities.split_blob_support] + /// field accordingly. + /// + /// Clients MUST check that the server supports this capability, before using + /// it. + /// + /// Clients SHOULD verify that the digest of the blob assembled by the fetched + /// chunks is equal to the requested blob digest. + /// + /// The lifetimes of the generated chunk blobs MAY be independent of the + /// lifetime of the original blob. In particular: + /// * A blob and any chunk derived from it MAY be evicted from the CAS at + /// different times. + /// * A call to [SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// extends the lifetime of the original blob, and sets the lifetimes of + /// the resulting chunks (or extends the lifetimes of already-existing + /// chunks). + /// * Touching a chunk extends its lifetime, but the server MAY choose not + /// to extend the lifetime of the original blob. + /// * Touching the original blob extends its lifetime, but the server MAY + /// choose not to extend the lifetimes of chunks derived from it. + /// + /// When blob splitting and splicing is used at the same time, the clients and + /// the server SHOULD agree out-of-band upon a chunking algorithm used by both + /// parties to benefit from each other's chunk data and avoid unnecessary data + /// duplication. + /// + /// Errors: + /// + /// * `NOT_FOUND`: The requested blob is not present in the CAS, OR there is no + /// split information available for the blob, OR at least one chunk needed to + /// reconstruct the blob is missing from the CAS. + /// * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the blob + /// chunks. + async fn split_blob( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// SpliceBlob tells the CAS how chunks can compose a blob. + /// + /// This is the complementary operation to the + /// [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// function to handle the chunked upload of large blobs to save upload + /// traffic. + /// + /// When uploading a large blob using chunked upload, clients MUST first upload + /// all chunks to the CAS, then call this RPC to tell the server how those chunks + /// compose the original blob. The chunks referenced in the SpliceBlob call SHOULD be + /// available in the CAS before calling this RPC. + /// + /// If a client needs to upload a large blob and is able to split a blob into + /// chunks in such a way that reusable chunks are obtained, e.g., by means of + /// content-defined chunking, it can first determine which parts of the blob + /// are already available in the remote CAS and upload the missing chunks, and + /// then use this API to store information on how the chunks compose the + /// original blob. + /// + /// Servers which implement this functionality MUST declare that they support + /// it by setting the + /// [CacheCapabilities.splice_blob_support][build.bazel.remote.execution.v2.CacheCapabilities.splice_blob_support] + /// field accordingly. + /// + /// Clients MUST check that the server supports this capability, before using + /// it. + /// + /// In order to ensure data consistency of the CAS, the server MUST only add + /// blobs to the CAS after verifying their digests. In particular, servers MUST NOT + /// trust digests provided by the client. The server MAY accept a request as no-op + /// if the client-specified blob is already in CAS or if information on how to + /// construct the blob from chunks is available. If the client-specified blob is + /// not already in the CAS, the server MUST verify that the digest of the newly + /// created blob assembled from chunks matches the digest specified by the + /// client, and reject the request if they differ. Servers MAY choose to allow + /// overwriting existing chunk mappings or to store multiple chunk mappings for + /// the same blob. + /// + /// When blob splitting and splicing is used at the same time, the clients and + /// the server SHOULD agree out-of-band upon a chunking algorithm used by both + /// parties to benefit from each other's chunk data and avoid unnecessary data + /// duplication. + /// + /// Errors: + /// + /// * `NOT_FOUND`: At least one of the blob chunks is not present in the CAS. + /// * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the + /// spliced blob. + /// * `INVALID_ARGUMENT`: The digest of the spliced blob is different from the + /// provided expected digest. + /// * `ALREADY_EXISTS`: The blob already exists in CAS and the server did not + /// extend the lifetime of the chunks specified in the request, e.g. because + /// it prefers a different chunking and extended those instead. Clients can + /// call [SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob] + /// to check what chunk mapping the server is using. + async fn splice_blob( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } /// The CAS (content-addressable storage) is used to store the inputs to and /// outputs from the execution service. Each piece of content is addressed by the @@ -3876,6 +4447,104 @@ pub mod content_addressable_storage_server { }; Box::pin(fut) } + "/build.bazel.remote.execution.v2.ContentAddressableStorage/SplitBlob" => { + #[allow(non_camel_case_types)] + struct SplitBlobSvc(pub Arc); + impl< + T: ContentAddressableStorage, + > tonic::server::UnaryService + for SplitBlobSvc { + type Response = super::SplitBlobResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::split_blob( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SplitBlobSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/build.bazel.remote.execution.v2.ContentAddressableStorage/SpliceBlob" => { + #[allow(non_camel_case_types)] + struct SpliceBlobSvc(pub Arc); + impl< + T: ContentAddressableStorage, + > tonic::server::UnaryService + for SpliceBlobSvc { + type Response = super::SpliceBlobResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::splice_blob( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SpliceBlobSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { let mut response = http::Response::new( diff --git a/nativelink-service/BUILD.bazel b/nativelink-service/BUILD.bazel index 2d13c305b..80bc06dd4 100644 --- a/nativelink-service/BUILD.bazel +++ b/nativelink-service/BUILD.bazel @@ -34,6 +34,7 @@ rust_library( "//nativelink-util", "@crates//:axum", "@crates//:bytes", + "@crates//:fastcdc", "@crates//:futures", "@crates//:http-body-util", "@crates//:hyper-1.7.0", @@ -46,6 +47,7 @@ rust_library( "@crates//:serde_json5", "@crates//:sha2", "@crates//:tokio", + "@crates//:tokio-util", "@crates//:tonic", "@crates//:tower", "@crates//:tracing", @@ -62,11 +64,15 @@ rust_test_suite( "tests/bytestream_server_test.rs", "tests/cas_server_test.rs", "tests/execution_server_test.rs", + "tests/fastcdc_conformance_test.rs", "tests/fetch_server_test.rs", "tests/health_server_test.rs", "tests/push_server_test.rs", "tests/worker_api_server_test.rs", ], + compile_data = glob(["tests/data/**"]) + [ + "//nativelink-util:tests/data/SekienAkashita.jpg", + ], proc_macro_deps = [ "//nativelink-macro", "@crates//:async-trait", @@ -83,6 +89,7 @@ rust_test_suite( "@crates//:async-lock", "@crates//:axum", "@crates//:bytes", + "@crates//:fastcdc", "@crates//:futures", "@crates//:hex", "@crates//:http-body-util", diff --git a/nativelink-service/Cargo.toml b/nativelink-service/Cargo.toml index e7369f8f8..13efb8a12 100644 --- a/nativelink-service/Cargo.toml +++ b/nativelink-service/Cargo.toml @@ -17,6 +17,7 @@ nativelink-util = { path = "../nativelink-util" } axum = { version = "0.8.3", default-features = false } bytes = { version = "1.10.1", default-features = false } +fastcdc = { version = "3.2.1", default-features = false, features = ["tokio"] } futures = { version = "0.3.31", default-features = false } http-body-util = { version = "0.1.3", default-features = false } hyper = { version = "1.6.0", default-features = false } @@ -43,6 +44,7 @@ tokio = { version = "1.52.2", features = [ tokio-stream = { version = "0.1.17", features = [ "fs", ], default-features = false } +tokio-util = { version = "0.7.14", features = ["io"], default-features = false } tonic = { version = "0.14.0", features = [ "gzip", "router", diff --git a/nativelink-service/src/capabilities_server.rs b/nativelink-service/src/capabilities_server.rs index 6e4102033..f2b32bfd4 100644 --- a/nativelink-service/src/capabilities_server.rs +++ b/nativelink-service/src/capabilities_server.rs @@ -15,7 +15,9 @@ use std::collections::HashMap; use std::sync::Arc; -use nativelink_config::cas_server::{CapabilitiesConfig, InstanceName, WithInstanceName}; +use nativelink_config::cas_server::{ + CapabilitiesConfig, CasStoreConfig, InstanceName, WithInstanceName, +}; use nativelink_error::{Error, ResultExt}; use nativelink_proto::build::bazel::remote::execution::v2::capabilities_server::{ Capabilities, CapabilitiesServer as Server, @@ -24,7 +26,7 @@ use nativelink_proto::build::bazel::remote::execution::v2::digest_function::Valu use nativelink_proto::build::bazel::remote::execution::v2::priority_capabilities::PriorityRange; use nativelink_proto::build::bazel::remote::execution::v2::symlink_absolute_path_strategy::Value as SymlinkAbsolutePathStrategy; use nativelink_proto::build::bazel::remote::execution::v2::{ - ActionCacheUpdateCapabilities, CacheCapabilities, ExecutionCapabilities, + ActionCacheUpdateCapabilities, CacheCapabilities, ExecutionCapabilities, FastCdc2020Params, GetCapabilitiesRequest, PriorityCapabilities, ServerCapabilities, }; use nativelink_proto::build::bazel::semver::SemVer; @@ -38,13 +40,36 @@ const MAX_BATCH_TOTAL_SIZE: i64 = 64 * 1024; #[derive(Debug, Default)] pub struct CapabilitiesServer { supported_node_properties_for_instance: HashMap>, + chunking_params_for_instance: HashMap, } impl CapabilitiesServer { pub async fn new( configs: &[WithInstanceName], scheduler_map: &HashMap>, + cas_configs: &[WithInstanceName], ) -> Result { + let mut chunking_params_for_instance = HashMap::new(); + for cas_config in cas_configs { + if let Some(chunking_config) = &cas_config.experimental_chunking { + let avg_chunk_size_bytes = chunking_config + .validated_avg_chunk_size_bytes() + .err_tip(|| { + format!( + "In 'experimental_chunking' of instance '{}'", + cas_config.instance_name + ) + })?; + chunking_params_for_instance.insert( + cas_config.instance_name.clone(), + FastCdc2020Params { + avg_chunk_size_bytes, + seed: 0, + }, + ); + } + } + let mut supported_node_properties_for_instance = HashMap::new(); for config in configs { let mut properties = Vec::new(); @@ -75,6 +100,7 @@ impl CapabilitiesServer { } Ok(Self { supported_node_properties_for_instance, + chunking_params_for_instance, }) } @@ -119,6 +145,7 @@ impl Capabilities for CapabilitiesServer { ], }); + let chunking_params = self.chunking_params_for_instance.get(&instance_name); let resp = ServerCapabilities { cache_capabilities: Some(CacheCapabilities { digest_functions: vec![ @@ -133,6 +160,11 @@ impl Capabilities for CapabilitiesServer { symlink_absolute_path_strategy: SymlinkAbsolutePathStrategy::Disallowed.into(), supported_compressors: vec![], supported_batch_update_compressors: vec![], + max_cas_blob_size_bytes: 0, + split_blob_support: chunking_params.is_some(), + splice_blob_support: chunking_params.is_some(), + fast_cdc_2020_params: chunking_params.copied(), + rep_max_cdc_params: None, }), execution_capabilities, deprecated_api_version: None, diff --git a/nativelink-service/src/cas_server.rs b/nativelink-service/src/cas_server.rs index 68e146686..f79df7f12 100644 --- a/nativelink-service/src/cas_server.rs +++ b/nativelink-service/src/cas_server.rs @@ -13,38 +13,165 @@ // limitations under the License. use core::convert::Into; -use core::pin::Pin; +use core::pin::{Pin, pin}; +use core::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use core::time::Duration; use std::collections::{HashMap, VecDeque}; use bytes::Bytes; +use fastcdc::v2020::{AsyncStreamCDC, Normalization}; use futures::stream::{FuturesUnordered, Stream}; use futures::{StreamExt, TryStreamExt}; use nativelink_config::cas_server::{CasStoreConfig, WithInstanceName}; use nativelink_error::{Code, Error, ResultExt, error_if, make_err, make_input_err}; +use nativelink_metric::{ + MetricFieldData, MetricKind, MetricPublishKnownKindData, MetricsComponent, group, publish, +}; use nativelink_proto::build::bazel::remote::execution::v2::content_addressable_storage_server::{ ContentAddressableStorage, ContentAddressableStorageServer as Server, }; use nativelink_proto::build::bazel::remote::execution::v2::{ BatchReadBlobsRequest, BatchReadBlobsResponse, BatchUpdateBlobsRequest, - BatchUpdateBlobsResponse, Directory, FindMissingBlobsRequest, FindMissingBlobsResponse, - GetTreeRequest, GetTreeResponse, batch_read_blobs_response, batch_update_blobs_response, - compressor, + BatchUpdateBlobsResponse, Digest, Directory, FindMissingBlobsRequest, FindMissingBlobsResponse, + GetTreeRequest, GetTreeResponse, SpliceBlobRequest, SpliceBlobResponse, SplitBlobRequest, + SplitBlobResponse, batch_read_blobs_response, batch_update_blobs_response, chunking_function, + compressor, digest_function, }; use nativelink_proto::google::rpc::Status as GrpcStatus; use nativelink_store::ac_utils::get_and_decode_digest; use nativelink_store::grpc_store::GrpcStore; use nativelink_store::store_manager::StoreManager; +use nativelink_util::buf_channel::make_buf_channel_pair; use nativelink_util::common::DigestInfo; -use nativelink_util::digest_hasher::make_ctx_for_hash_func; -use nativelink_util::store_trait::{Store, StoreLike}; +use nativelink_util::digest_hasher::{DigestHasher, DigestHasherFunc, make_ctx_for_hash_func}; +use nativelink_util::store_trait::{Store, StoreLike, UploadSizeInfo}; use opentelemetry::context::FutureExt; +use prost::Message; +use tokio_util::io::StreamReader; use tonic::{Request, Response, Status}; use tracing::{Instrument, Level, debug, error_span, instrument}; +/// Metrics for the experimental `SplitBlob`/`SpliceBlob` chunking RPCs. +/// The split hit rate (`split_hits` / `split_requests_total`) indicates how +/// often chunked downloads could be served; the spliced/split byte totals +/// bound the transfer volume flowing through the chunked paths. +#[derive(Debug, Default)] +pub struct ChunkingMetrics { + /// Total `SpliceBlob` requests received on chunking-enabled instances. + pub splice_requests_total: AtomicU64, + /// `SpliceBlob` requests that were no-ops because the blob and its chunk + /// layout were already registered. + pub splice_already_exists: AtomicU64, + /// `SpliceBlob` requests rejected because the re-assembled blob did not + /// match the expected digest or size. + pub splice_verification_failures: AtomicU64, + /// Total bytes of blobs successfully re-assembled by `SpliceBlob`. + pub splice_bytes_total: AtomicU64, + /// Total `SplitBlob` requests received on chunking-enabled instances. + pub split_requests_total: AtomicU64, + /// `SplitBlob` requests served from a stored chunk layout. + pub split_hits: AtomicU64, + /// `SplitBlob` requests that could not be served because the blob was + /// not present in the CAS. + pub split_misses: AtomicU64, + /// `SplitBlob` requests served by chunking the blob on demand because + /// no stored layout was available (or its chunks were evicted). + pub split_chunked_on_demand: AtomicU64, + /// Total bytes of blobs served as chunk layouts by `SplitBlob`. + pub split_bytes_total: AtomicU64, +} + +impl MetricsComponent for ChunkingMetrics { + fn publish( + &self, + _kind: MetricKind, + field_metadata: MetricFieldData, + ) -> Result { + let _enter = group!(field_metadata.name).entered(); + + publish!( + "splice_requests_total", + &self.splice_requests_total, + MetricKind::Counter, + "Total SpliceBlob requests received" + ); + publish!( + "splice_already_exists", + &self.splice_already_exists, + MetricKind::Counter, + "SpliceBlob requests that were no-ops because blob and layout already existed" + ); + publish!( + "splice_verification_failures", + &self.splice_verification_failures, + MetricKind::Counter, + "SpliceBlob requests rejected due to digest or size mismatch" + ); + publish!( + "splice_bytes_total", + &self.splice_bytes_total, + MetricKind::Counter, + "Total bytes of blobs re-assembled by SpliceBlob" + ); + publish!( + "split_requests_total", + &self.split_requests_total, + MetricKind::Counter, + "Total SplitBlob requests received" + ); + publish!( + "split_hits", + &self.split_hits, + MetricKind::Counter, + "SplitBlob requests served from a stored chunk layout" + ); + publish!( + "split_misses", + &self.split_misses, + MetricKind::Counter, + "SplitBlob requests where the blob was not present" + ); + publish!( + "split_chunked_on_demand", + &self.split_chunked_on_demand, + MetricKind::Counter, + "SplitBlob requests served by chunking the blob on demand" + ); + publish!( + "split_bytes_total", + &self.split_bytes_total, + MetricKind::Counter, + "Total bytes of blobs served as chunk layouts by SplitBlob" + ); + + Ok(MetricPublishKnownKindData::Component) + } +} + +/// Per-instance state for the experimental chunking RPCs. +#[derive(Debug, Clone)] +struct ChunkingInstance { + /// Store holding blob-digest -> chunk-layout mappings. + index_store: Store, + /// Average chunk size used for server-side `FastCDC` 2020 chunking. + avg_chunk_size_bytes: u32, + /// Maximum number of chunks accepted in a `SpliceBlob` request or + /// produced by on-demand chunking. + max_chunk_count: usize, +} + +impl ChunkingInstance { + /// Maximum serialized layout size consistent with `max_chunk_count`. + const fn max_layout_size(&self) -> u64 { + self.max_chunk_count as u64 * MAX_LAYOUT_BYTES_PER_CHUNK + } +} + #[derive(Debug)] pub struct CasServer { stores: HashMap, + chunking_instances: HashMap, + chunking_metrics: ChunkingMetrics, } type GetTreeStream = Pin> + Send + 'static>>; @@ -52,25 +179,109 @@ type GetTreeStream = Pin> /// Per-blob deadline applied inside `BatchReadBlobs` / `BatchUpdateBlobs`. const BATCH_PER_BLOB_TIMEOUT: Duration = Duration::from_secs(30); +/// Maximum size of a single chunk accepted in a `SpliceBlob` request. +/// Deliberately looser than the largest chunk the server ever advertises +/// (4x the maximum allowed average = 4 MiB) so clients using their own +/// chunking function are still accepted. Together with `CHUNK_CONCURRENCY` +/// this bounds the memory a single splice request can pin. +const MAX_SPLICE_CHUNK_SIZE: u64 = 16 * 1024 * 1024; + +/// Generous upper bound for the serialized size of one chunk entry in a +/// stored layout (hash string of up to 128 hex characters plus varints and +/// field tags). Multiplied by the configured `max_chunk_count` this caps +/// layout reads from the index store; a larger entry is corrupt. A truncated +/// read is detected (and treated as no layout) by the size consistency check +/// in `read_chunk_layout`. +const MAX_LAYOUT_BYTES_PER_CHUNK: u64 = 160; + +/// Number of chunk reads/writes kept in flight while re-assembling or +/// chunking a blob. Matches the `DedupStore` concurrency default. +const CHUNK_CONCURRENCY: usize = 10; + impl CasServer { pub fn new( configs: &[WithInstanceName], store_manager: &StoreManager, ) -> Result { let mut stores = HashMap::with_capacity(configs.len()); + let mut chunking_instances = HashMap::new(); for config in configs { let store = store_manager.get_store(&config.cas_store).ok_or_else(|| { make_input_err!("'cas_store': '{}' does not exist", config.cas_store) })?; + if let Some(chunking_config) = &config.experimental_chunking { + let avg_chunk_size_bytes = chunking_config + .validated_avg_chunk_size_bytes() + .err_tip(|| { + format!( + "In 'experimental_chunking' of instance '{}'", + config.instance_name + ) + })?; + if store.downcast_ref::(None).is_some() { + // SplitBlob/SpliceBlob for grpc-store-backed instances + // are forwarded to the backend, which owns the chunk + // layouts; a local index store is meaningless there. + error_if!( + chunking_config.index_store.is_some(), + "'experimental_chunking.index_store' of instance '{}' must not be set when 'cas_store' is a grpc store: SplitBlob/SpliceBlob are forwarded to the backend", + config.instance_name + ); + // No ChunkingInstance: the forwarding shortcut in the + // handlers takes over before local chunking is reached. + stores.insert(config.instance_name.clone(), store); + continue; + } + let index_store_name = chunking_config.index_store.as_ref().ok_or_else(|| { + make_input_err!( + "'experimental_chunking.index_store' of instance '{}' is required", + config.instance_name + ) + })?; + // Chunk layouts are stored under the digests of the blobs + // they describe but do not hash to them, so writing them + // into the CAS itself would overwrite blob content. + error_if!( + index_store_name == &config.cas_store, + "'experimental_chunking.index_store' of instance '{}' must not be the same store as 'cas_store'", + config.instance_name + ); + let index_store = store_manager.get_store(index_store_name).ok_or_else(|| { + make_input_err!( + "'experimental_chunking.index_store': '{index_store_name}' does not exist" + ) + })?; + let avg_chunk_size_bytes = u32::try_from(avg_chunk_size_bytes) + .err_tip(|| "avg_chunk_size_bytes did not fit in u32")?; + let max_chunk_count = usize::try_from(chunking_config.resolved_max_chunk_count()) + .err_tip(|| "max_chunk_count did not fit in usize")?; + chunking_instances.insert( + config.instance_name.clone(), + ChunkingInstance { + index_store, + avg_chunk_size_bytes, + max_chunk_count, + }, + ); + } stores.insert(config.instance_name.clone(), store); } - Ok(Self { stores }) + Ok(Self { + stores, + chunking_instances, + chunking_metrics: ChunkingMetrics::default(), + }) } pub fn into_service(self) -> Server { Server::new(self) } + /// Metrics for the experimental `SplitBlob`/`SpliceBlob` RPCs. + pub const fn chunking_metrics(&self) -> &ChunkingMetrics { + &self.chunking_metrics + } + async fn inner_find_missing_blobs( &self, request: FindMissingBlobsRequest, @@ -330,6 +541,536 @@ impl CasServer { }) .right_stream()) } + + /// Returns the CAS store for an instance and its chunking state, or + /// `Unimplemented` when chunking is not enabled for it. Grpc-store-backed + /// instances never reach this: their handlers forward the RPC to the + /// backend first. + fn chunking_instance(&self, instance_name: &str) -> Result<(Store, ChunkingInstance), Error> { + let store = self + .stores + .get(instance_name) + .err_tip(|| format!("'instance_name' not configured for '{instance_name}'"))? + .clone(); + let chunking_instance = self + .chunking_instances + .get(instance_name) + .ok_or_else(|| { + make_err!( + Code::Unimplemented, + "Blob chunking is not enabled for instance '{instance_name}'" + ) + })? + .clone(); + Ok((store, chunking_instance)) + } + + /// Returns the backend `GrpcStore` when the instance's CAS is a grpc + /// proxy store, in which case chunking RPCs are forwarded verbatim. + fn grpc_store_for_instance(&self, instance_name: &str) -> Option<&GrpcStore> { + self.stores + .get(instance_name) + .and_then(|store| store.downcast_ref::(None)) + } + + /// Returns the digest function explicitly requested by the client, or + /// `None` when the field was left unset. REAPI's length-based inference + /// cannot be used as a fallback here: SHA256 and BLAKE3 digests are both + /// 32 bytes, and `NativeLink` announces support for both. Notably Bazel + /// (9.1.1) leaves this field unset even when running with + /// `--digest_function=blake3`. + fn explicit_hasher_func(digest_function_value: i32) -> Option { + digest_function::Value::try_from(digest_function_value) + .ok() + .and_then(|value| DigestHasherFunc::try_from(value).ok()) + } + + /// Determines the digest function of a blob already present in the CAS + /// by hashing its content with each supported function and returning the + /// one that reproduces `blob_digest`. + async fn infer_blob_hasher_func( + store: &Store, + blob_digest: DigestInfo, + ) -> Result { + const CANDIDATES: [DigestHasherFunc; 2] = + [DigestHasherFunc::Sha256, DigestHasherFunc::Blake3]; + let (tx, rx) = make_buf_channel_pair(); + let read_store = store.clone(); + let read_fut = async move { + let mut tx = tx; + read_store + .get_part(blob_digest, &mut tx, 0, None) + .await + .err_tip(|| "Failed to read blob in infer_blob_hasher_func") + }; + let hash_fut = async move { + let mut rx = rx; + let mut hashers = CANDIDATES.map(|func| func.hasher()); + loop { + let data = rx + .recv() + .await + .err_tip(|| "In infer_blob_hasher_func::recv")?; + if data.is_empty() { + break; // EOF. + } + for hasher in &mut hashers { + hasher.update(&data); + } + } + Ok::<_, Error>(hashers.map(|mut hasher| hasher.finalize_digest())) + }; + let (read_res, hash_res) = futures::join!(read_fut, hash_fut); + let computed_digests = read_res.merge(hash_res)?; + CANDIDATES + .iter() + .zip(computed_digests) + .find(|(_, computed)| *computed == blob_digest) + .map(|(func, _)| *func) + .ok_or_else(|| { + make_err!( + Code::NotFound, + "Blob {blob_digest} does not match any supported digest function; no split information available" + ) + }) + } + + /// Returns the display names of the chunks missing from the CAS. The + /// existence check also touches present chunks, which extends their + /// lifetimes on a best-effort basis (stores that answer existence from a + /// cache may not promote the underlying entries). + async fn missing_chunks(store: &Store, chunk_digests: &[Digest]) -> Result, Error> { + let mut digest_infos = Vec::with_capacity(chunk_digests.len()); + for digest in chunk_digests { + digest_infos + .push(DigestInfo::try_from(digest.clone()).err_tip(|| "Invalid chunk digest")?); + } + let chunk_keys: Vec<_> = digest_infos.iter().map(|digest| (*digest).into()).collect(); + let sizes = store + .has_many(&chunk_keys) + .await + .err_tip(|| "In missing_chunks")?; + Ok(sizes + .iter() + .zip(&digest_infos) + .filter(|(maybe_size, _)| maybe_size.is_none()) + .map(|(_, digest)| digest.to_string()) + .collect()) + } + + /// Reads the chunk layout registered for a blob. Returns `None` when no + /// usable layout exists: not registered, undecodable, or inconsistent + /// with the blob size (which also rejects entries truncated by the read + /// cap below). + async fn read_chunk_layout( + chunking_instance: &ChunkingInstance, + blob_digest: DigestInfo, + ) -> Option { + let layout_bytes = chunking_instance + .index_store + .get_part_unchunked(blob_digest, 0, Some(chunking_instance.max_layout_size())) + .await + .ok()?; + let layout = SplitBlobResponse::decode(layout_bytes).ok()?; + // A usable layout must reproduce the blob exactly, so the chunk + // sizes have to add up to the blob size. + let mut total_size: u64 = 0; + for digest in &layout.chunk_digests { + total_size = total_size.checked_add(u64::try_from(digest.size_bytes).ok()?)?; + } + (total_size == blob_digest.size_bytes()).then_some(layout) + } + + /// Writes the chunk layout for a blob to the index store. This is the + /// write side of the format `read_chunk_layout` expects. + async fn write_chunk_layout( + index_store: &Store, + blob_digest: DigestInfo, + layout: &SplitBlobResponse, + ) -> Result<(), Error> { + index_store + .update_oneshot(blob_digest, layout.encode_to_vec().into()) + .await + .err_tip(|| "Failed to write chunk layout to index store") + } + + async fn inner_split_blob( + &self, + request: SplitBlobRequest, + ) -> Result, Error> { + // If we are a GrpcStore we forward the RPC to the backend, which + // owns chunking and the layout index for proxied instances. + if let Some(grpc_store) = self.grpc_store_for_instance(&request.instance_name) { + return grpc_store.split_blob(Request::new(request)).await; + } + let (store, chunking_instance) = self.chunking_instance(&request.instance_name)?; + self.chunking_metrics + .split_requests_total + .fetch_add(1, Ordering::Relaxed); + + let blob_digest: DigestInfo = request + .blob_digest + .err_tip(|| "Expected blob_digest to exist in SplitBlobRequest")? + .try_into() + .err_tip(|| "In SplitBlobRequest::blob_digest")?; + + // The existence check also touches the blob, extending its lifetime + // (best effort) as suggested by the REAPI spec for SplitBlob. + let (blob_exists, maybe_layout) = futures::join!( + store.has(blob_digest), + Self::read_chunk_layout(&chunking_instance, blob_digest), + ); + if blob_exists.err_tip(|| "In split_blob")?.is_none() { + self.chunking_metrics + .split_misses + .fetch_add(1, Ordering::Relaxed); + return Err(make_err!( + Code::NotFound, + "Blob {blob_digest} not present in the CAS in split_blob" + )); + } + + // Serve the registered layout if it is still fully backed by chunks + // in the CAS. Any problem with it (missing, corrupt, evicted chunks, + // or a transient chunk existence-check failure) falls back to + // re-chunking the blob below. + if let Some(layout) = maybe_layout + && matches!( + Self::missing_chunks(&store, &layout.chunk_digests).await, + Ok(missing) if missing.is_empty() + ) + { + self.chunking_metrics + .split_hits + .fetch_add(1, Ordering::Relaxed); + self.chunking_metrics + .split_bytes_total + .fetch_add(blob_digest.size_bytes(), Ordering::Relaxed); + return Ok(Response::new(layout)); + } + + // No usable layout: chunk the blob on demand with FastCDC 2020, + // store the chunks and the layout, and serve the result. This is the + // path taken for blobs that were uploaded whole (e.g. outputs + // produced by remote execution workers). + let split_response = self + .chunk_blob_on_demand( + &store, + &chunking_instance, + blob_digest, + request.digest_function, + ) + .await?; + self.chunking_metrics + .split_chunked_on_demand + .fetch_add(1, Ordering::Relaxed); + self.chunking_metrics + .split_bytes_total + .fetch_add(blob_digest.size_bytes(), Ordering::Relaxed); + Ok(Response::new(split_response)) + } + + /// Chunks the blob with `FastCDC` 2020 (normalization level 2, parameters + /// derived from the configured average chunk size per the REAPI spec), + /// uploads any missing chunks to the CAS, registers the layout in the + /// index store, and returns it. + async fn chunk_blob_on_demand( + &self, + store: &Store, + chunking_instance: &ChunkingInstance, + blob_digest: DigestInfo, + digest_function_value: i32, + ) -> Result { + let avg_size = chunking_instance.avg_chunk_size_bytes; + let (min_size, max_size) = (avg_size / 4, avg_size * 4); + // Chunk digests MUST use the blob's digest function. When the client + // leaves the field unset it has to be inferred from the blob content + // (an extra read pass) since the hash length alone is ambiguous. + let hasher_func = match Self::explicit_hasher_func(digest_function_value) { + Some(hasher_func) => hasher_func, + None => Self::infer_blob_hasher_func(store, blob_digest).await?, + }; + + let (tx, rx) = make_buf_channel_pair(); + let read_store = store.clone(); + // `tx` is moved into the future so that when the read finishes or + // fails it is dropped, which terminates the chunking stream. + let read_fut = async move { + let mut tx = tx; + read_store + .get_part(blob_digest, &mut tx, 0, None) + .await + .err_tip(|| format!("Failed to read blob {blob_digest} in chunk_blob_on_demand")) + }; + // `rx` is owned by this future so an early error return drops it, + // which aborts the in-flight read instead of leaving it blocked. + let chunk_fut = async move { + let mut bytes_reader = StreamReader::new(rx); + let mut cdc = AsyncStreamCDC::with_level( + &mut bytes_reader, + min_size, + avg_size, + max_size, + Normalization::Level2, + ); + // Chunks are hashed and stored CHUNK_CONCURRENCY at a time while + // the blob keeps streaming; `buffered` preserves chunk order. + let chunk_digests: Vec = pin!(cdc.as_stream()) + .map(|chunk_result| async { + let chunk = chunk_result + .map_err(|e| make_err!(Code::Internal, "Failed to chunk blob: {e:?}")) + .err_tip(|| "In chunk_blob_on_demand")?; + let mut hasher = hasher_func.hasher(); + hasher.update(&chunk.data); + let chunk_digest = hasher.finalize_digest(); + // The existence check also touches pre-existing chunks, + // extending their lifetimes (best effort). FastCDC is + // deterministic, so repeated splits of similar blobs + // mostly find their chunks present. + if store + .has(chunk_digest) + .await + .err_tip(|| "In chunk_blob_on_demand")? + .is_none() + { + store + .update_oneshot(chunk_digest, chunk.data.into()) + .await + .err_tip(|| { + format!( + "Failed to store chunk {chunk_digest} in chunk_blob_on_demand" + ) + })?; + } + Ok::(chunk_digest.into()) + }) + .buffered(CHUNK_CONCURRENCY) + .try_collect() + .await?; + Ok::, Error>(chunk_digests) + }; + let (read_res, chunk_res) = futures::join!(read_fut, chunk_fut); + // Prefer the read error (the chunker error is usually a consequence + // of it); merge keeps both messages when both fail. + let chunk_digests = read_res + .merge(chunk_res) + .err_tip(|| "Failed to chunk blob in chunk_blob_on_demand")?; + if chunk_digests.len() > chunking_instance.max_chunk_count { + return Err(make_err!( + Code::NotFound, + "Blob {blob_digest} produced {} chunks, exceeding the configured max_chunk_count of {}; no split information available", + chunk_digests.len(), + chunking_instance.max_chunk_count + )); + } + + let split_response = SplitBlobResponse { + chunk_digests, + chunking_function: chunking_function::Value::FastCdc2020.into(), + }; + Self::write_chunk_layout(&chunking_instance.index_store, blob_digest, &split_response) + .await?; + Ok(split_response) + } + + async fn inner_splice_blob( + &self, + request: SpliceBlobRequest, + ) -> Result, Error> { + // If we are a GrpcStore we forward the RPC to the backend, which + // owns chunking and the layout index for proxied instances. + if let Some(grpc_store) = self.grpc_store_for_instance(&request.instance_name) { + return grpc_store.splice_blob(Request::new(request)).await; + } + let (store, chunking_instance) = self.chunking_instance(&request.instance_name)?; + let index_store = chunking_instance.index_store; + self.chunking_metrics + .splice_requests_total + .fetch_add(1, Ordering::Relaxed); + + let blob_digest: DigestInfo = request + .blob_digest + .err_tip(|| "Expected blob_digest to exist in SpliceBlobRequest")? + .try_into() + .err_tip(|| "In SpliceBlobRequest::blob_digest")?; + + error_if!( + request.chunk_digests.is_empty(), + "chunk_digests must not be empty in splice_blob" + ); + error_if!( + request.chunk_digests.len() > chunking_instance.max_chunk_count, + "Request has {} chunk_digests, expected at most {} in splice_blob", + request.chunk_digests.len(), + chunking_instance.max_chunk_count + ); + let mut chunk_digests = Vec::with_capacity(request.chunk_digests.len()); + let mut total_size: u64 = 0; + for digest in &request.chunk_digests { + let digest_info = DigestInfo::try_from(digest.clone()) + .err_tip(|| "In SpliceBlobRequest::chunk_digests")?; + error_if!( + digest_info.size_bytes() == 0 || digest_info.size_bytes() > MAX_SPLICE_CHUNK_SIZE, + "Chunk {digest_info} has invalid size, expected to be in range (0, {MAX_SPLICE_CHUNK_SIZE}] in splice_blob" + ); + total_size += digest_info.size_bytes(); + chunk_digests.push(digest_info); + } + if total_size != blob_digest.size_bytes() { + self.chunking_metrics + .splice_verification_failures + .fetch_add(1, Ordering::Relaxed); + return Err(make_err!( + Code::InvalidArgument, + "Sum of chunk sizes ({total_size}) does not match the expected blob size ({}) in splice_blob", + blob_digest.size_bytes() + )); + } + + // One round of existence checks: the chunks (which also touches + // them, best-effort extending their lifetimes), the blob, and the + // registered layout. + let (missing_chunks, blob_exists, layout_exists) = futures::join!( + Self::missing_chunks(&store, &request.chunk_digests), + store.has(blob_digest), + index_store.has(blob_digest), + ); + let missing_chunks = missing_chunks.err_tip(|| "In splice_blob")?; + if !missing_chunks.is_empty() { + return Err(make_err!( + Code::NotFound, + "Chunk(s) [{}] not present in the CAS in splice_blob", + missing_chunks.join(", ") + )); + } + // Fast path: if the blob and its chunk layout are already registered + // this request is a no-op. + if blob_exists.err_tip(|| "In splice_blob")?.is_some() + && layout_exists.err_tip(|| "In splice_blob")?.is_some() + { + self.chunking_metrics + .splice_already_exists + .fetch_add(1, Ordering::Relaxed); + return Ok(Response::new(SpliceBlobResponse { + blob_digest: Some(blob_digest.into()), + })); + } + + // Re-assemble the blob into the store: chunk reads are pipelined + // CHUNK_CONCURRENCY at a time while hashing and channel writes stay + // in chunk order. The digest is verified before the final EOF is + // sent, so a digest mismatch aborts the upload before the store + // commits it. + // When the client sets the digest function, verify with exactly that + // function. When it is unset the hash length is ambiguous (SHA256 + // and BLAKE3 are both 32 bytes), so hash with both candidates and + // accept whichever reproduces the expected digest. + let candidate_hasher_funcs: Vec = + match Self::explicit_hasher_func(request.digest_function) { + Some(hasher_func) => vec![hasher_func], + None => vec![DigestHasherFunc::Sha256, DigestHasherFunc::Blake3], + }; + let verification_failed = AtomicBool::new(false); + let verification_failed_ref = &verification_failed; + let (tx, rx) = make_buf_channel_pair(); + let send_store = store.clone(); + // `tx` is moved into the future so that an early error return drops + // it without an EOF, which aborts the in-flight store update instead + // of leaving it waiting for more data. + let send_fut = async move { + let mut tx = tx; + let mut hashers: Vec<_> = candidate_hasher_funcs + .iter() + .map(DigestHasherFunc::hasher) + .collect(); + let mut fetch_stream = futures::stream::iter(chunk_digests.into_iter().map( + move |chunk_digest| { + let store = send_store.clone(); + async move { + let data = store + .get_part_unchunked(chunk_digest, 0, None) + .await + .err_tip(|| { + format!("Failed to read chunk {chunk_digest} in splice_blob") + })?; + if u64::try_from(data.len()).unwrap_or(0) != chunk_digest.size_bytes() { + return Err(make_err!( + Code::Internal, + "Chunk {chunk_digest} content has length {}, expected {}, in splice_blob", + data.len(), + chunk_digest.size_bytes() + )); + } + Ok::(data) + } + }, + )) + .buffered(CHUNK_CONCURRENCY); + while let Some(data) = fetch_stream.next().await { + let data = data?; + for hasher in &mut hashers { + hasher.update(&data); + } + tx.send(data) + .await + .err_tip(|| "Failed to send chunk data in splice_blob")?; + } + drop(fetch_stream); + let computed_digests: Vec = hashers + .iter_mut() + .map(DigestHasher::finalize_digest) + .collect(); + if !computed_digests + .iter() + .any(|computed| *computed == blob_digest) + { + verification_failed_ref.store(true, Ordering::Relaxed); + return Err(make_err!( + Code::InvalidArgument, + "Digest of spliced blob ({}) does not match the expected digest ({blob_digest}) in splice_blob", + computed_digests + .iter() + .map(ToString::to_string) + .collect::>() + .join(" / ") + )); + } + tx.send_eof() + .err_tip(|| "Failed to send EOF in splice_blob")?; + Ok::<(), Error>(()) + }; + let update_fut = store.update( + blob_digest, + rx, + UploadSizeInfo::ExactSize(blob_digest.size_bytes()), + ); + let (send_res, update_res) = futures::join!(send_fut, update_fut); + if verification_failed.load(Ordering::Relaxed) { + self.chunking_metrics + .splice_verification_failures + .fetch_add(1, Ordering::Relaxed); + } + // Prefer the sender error: it carries the reason the upload was + // aborted (e.g. the digest mismatch), the store error is usually a + // consequence; merge keeps both messages when both fail. + send_res + .merge(update_res) + .err_tip(|| "Failed to write spliced blob to store in splice_blob")?; + + // Persist the chunk layout so SplitBlob can serve it later. + let split_response = SplitBlobResponse { + chunk_digests: request.chunk_digests, + chunking_function: request.chunking_function, + }; + Self::write_chunk_layout(&index_store, blob_digest, &split_response).await?; + + self.chunking_metrics + .splice_bytes_total + .fetch_add(blob_digest.size_bytes(), Ordering::Relaxed); + Ok(Response::new(SpliceBlobResponse { + blob_digest: Some(blob_digest.into()), + })) + } } #[tonic::async_trait] @@ -443,4 +1184,59 @@ impl ContentAddressableStorage for CasServer { } resp } + + #[instrument( + err, + ret(level = Level::DEBUG), + level = Level::ERROR, + skip_all, + fields( + request.instance_name = ?grpc_request.get_ref().instance_name, + request.blob_digest = ?grpc_request.get_ref().blob_digest, + request.digest_function = ?grpc_request.get_ref().digest_function, + ) + )] + async fn split_blob( + &self, + grpc_request: Request, + ) -> Result, Status> { + let request = grpc_request.into_inner(); + let digest_function = request.digest_function; + self.inner_split_blob(request) + .instrument(error_span!("cas_server_split_blob")) + .with_context( + make_ctx_for_hash_func(digest_function).err_tip(|| "In CasServer::split_blob")?, + ) + .await + .err_tip(|| "Failed on split_blob() command") + .map_err(Into::into) + } + + #[instrument( + err, + ret(level = Level::DEBUG), + level = Level::ERROR, + skip_all, + fields( + // Skip request.chunk_digests which is sometimes enormous. + request.instance_name = ?grpc_request.get_ref().instance_name, + request.blob_digest = ?grpc_request.get_ref().blob_digest, + request.digest_function = ?grpc_request.get_ref().digest_function, + ) + )] + async fn splice_blob( + &self, + grpc_request: Request, + ) -> Result, Status> { + let request = grpc_request.into_inner(); + let digest_function = request.digest_function; + self.inner_splice_blob(request) + .instrument(error_span!("cas_server_splice_blob")) + .with_context( + make_ctx_for_hash_func(digest_function).err_tip(|| "In CasServer::splice_blob")?, + ) + .await + .err_tip(|| "Failed on splice_blob() command") + .map_err(Into::into) + } } diff --git a/nativelink-service/tests/cas_server_test.rs b/nativelink-service/tests/cas_server_test.rs index dccc90208..2e1b0c7af 100644 --- a/nativelink-service/tests/cas_server_test.rs +++ b/nativelink-service/tests/cas_server_test.rs @@ -13,6 +13,7 @@ // limitations under the License. use core::pin::Pin; +use core::sync::atomic::Ordering; use core::time::Duration; use std::sync::Arc; @@ -27,8 +28,9 @@ use nativelink_proto::build::bazel::remote::execution::v2::content_addressable_s use nativelink_proto::build::bazel::remote::execution::v2::{ BatchReadBlobsRequest, BatchReadBlobsResponse, BatchUpdateBlobsRequest, BatchUpdateBlobsResponse, Digest, Directory, DirectoryNode, FindMissingBlobsRequest, - GetTreeRequest, GetTreeResponse, NodeProperties, batch_read_blobs_response, - batch_update_blobs_request, batch_update_blobs_response, compressor, digest_function, + GetTreeRequest, GetTreeResponse, NodeProperties, SpliceBlobRequest, SplitBlobRequest, + SplitBlobResponse, batch_read_blobs_response, batch_update_blobs_request, + batch_update_blobs_response, chunking_function, compressor, digest_function, }; use nativelink_proto::google::rpc::Status as GrpcStatus; use nativelink_service::cas_server::CasServer; @@ -37,12 +39,13 @@ use nativelink_store::default_store_factory::store_factory; use nativelink_store::store_manager::StoreManager; use nativelink_util::buf_channel::{DropCloserReadHalf, DropCloserWriteHalf}; use nativelink_util::common::DigestInfo; -use nativelink_util::digest_hasher::DigestHasherFunc; +use nativelink_util::digest_hasher::{DigestHasher, DigestHasherFunc}; use nativelink_util::health_utils::{HealthStatusIndicator, default_health_status_indicator}; use nativelink_util::store_trait::{ RemoveItemCallback, Store, StoreDriver, StoreKey, StoreLike, UploadSizeInfo, }; use pretty_assertions::assert_eq; +use prost::Message; use prost_types::Timestamp; use tonic::{Code, Request}; @@ -72,6 +75,7 @@ fn make_cas_server(store_manager: &StoreManager) -> Result { instance_name: "foo_instance_name".to_string(), config: nativelink_config::cas_server::CasStoreConfig { cas_store: "main_cas".to_string(), + experimental_chunking: None, }, }], store_manager, @@ -747,6 +751,7 @@ fn make_cas_server_with_stall_store(delay: Duration) -> Result instance_name: INSTANCE_NAME.to_string(), config: nativelink_config::cas_server::CasStoreConfig { cas_store: "main_cas".to_string(), + experimental_chunking: None, }, }], &store_manager, @@ -835,3 +840,708 @@ async fn batch_read_blobs_per_blob_timeout_returns_deadline_exceeded() ); Ok(()) } + +const CHUNK1_VALUE: &str = "hello "; +const CHUNK2_VALUE: &str = "world"; + +async fn make_chunking_store_manager() -> Result, Error> { + let store_manager = make_store_manager().await?; + store_manager.add_store( + "chunk_index", + store_factory( + &StoreSpec::Memory(MemorySpec::default()), + &store_manager, + None, + ) + .await?, + ); + Ok(store_manager) +} + +fn make_chunking_cas_server(store_manager: &StoreManager) -> Result { + make_chunking_cas_server_with_avg(store_manager, 0) +} + +fn make_chunking_cas_server_with_avg( + store_manager: &StoreManager, + avg_chunk_size_bytes: u64, +) -> Result { + CasServer::new( + &[WithInstanceName { + instance_name: INSTANCE_NAME.to_string(), + config: nativelink_config::cas_server::CasStoreConfig { + cas_store: "main_cas".to_string(), + experimental_chunking: Some(nativelink_config::cas_server::CasChunkingConfig { + index_store: Some("chunk_index".to_string()), + avg_chunk_size_bytes, + max_chunk_count: 0, + }), + }, + }], + store_manager, + ) +} + +/// Uploads the two test chunks to the store and returns their digests and +/// the digest of their concatenation. +async fn upload_test_chunks(store: &Store) -> Result<(Digest, Digest, Digest), Error> { + let chunk1_digest = Digest { + hash: HASH1.to_string(), + size_bytes: CHUNK1_VALUE.len() as i64, + }; + let chunk2_digest = Digest { + hash: HASH2.to_string(), + size_bytes: CHUNK2_VALUE.len() as i64, + }; + store + .update_oneshot( + DigestInfo::try_from(chunk1_digest.clone())?, + CHUNK1_VALUE.into(), + ) + .await?; + store + .update_oneshot( + DigestInfo::try_from(chunk2_digest.clone())?, + CHUNK2_VALUE.into(), + ) + .await?; + let mut hasher = DigestHasherFunc::Sha256.hasher(); + hasher.update(CHUNK1_VALUE.as_bytes()); + hasher.update(CHUNK2_VALUE.as_bytes()); + let blob_digest: Digest = hasher.finalize_digest().into(); + Ok((chunk1_digest, chunk2_digest, blob_digest)) +} + +#[nativelink_test] +async fn splice_and_split_round_trip() -> Result<(), Box> { + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + + let (chunk1_digest, chunk2_digest, blob_digest) = upload_test_chunks(&store).await?; + + let splice_response = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest.clone()), + chunk_digests: vec![chunk1_digest.clone(), chunk2_digest.clone()], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(splice_response.blob_digest.as_ref(), Some(&blob_digest)); + + // The spliced blob must be materialized in the CAS so non-chunking + // clients can read it. + let blob_data = store + .get_part_unchunked(DigestInfo::try_from(blob_digest.clone())?, 0, None) + .await?; + assert_eq!(blob_data, format!("{CHUNK1_VALUE}{CHUNK2_VALUE}")); + + let split_response = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!( + split_response.chunk_digests, + vec![chunk1_digest, chunk2_digest] + ); + assert_eq!( + split_response.chunking_function, + i32::from(chunking_function::Value::FastCdc2020) + ); + + let metrics = cas_server.chunking_metrics(); + assert_eq!(metrics.splice_requests_total.load(Ordering::Relaxed), 1); + assert_eq!( + metrics.splice_bytes_total.load(Ordering::Relaxed), + (CHUNK1_VALUE.len() + CHUNK2_VALUE.len()) as u64 + ); + assert_eq!(metrics.split_requests_total.load(Ordering::Relaxed), 1); + assert_eq!(metrics.split_hits.load(Ordering::Relaxed), 1); + assert_eq!(metrics.split_misses.load(Ordering::Relaxed), 0); + Ok(()) +} + +#[nativelink_test] +async fn splice_blob_rejects_digest_mismatch() -> Result<(), Box> { + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + + let (chunk1_digest, chunk2_digest, _blob_digest) = upload_test_chunks(&store).await?; + let total_size = chunk1_digest.size_bytes + chunk2_digest.size_bytes; + let wrong_blob_digest = Digest { + hash: HASH3.to_string(), + size_bytes: total_size, + }; + + let status = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(wrong_blob_digest.clone()), + chunk_digests: vec![chunk1_digest, chunk2_digest], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(status.code(), Code::InvalidArgument); + assert!( + status + .message() + .contains("does not match the expected digest"), + "unexpected message: {}", + status.message() + ); + + // The blob must not have been committed to the CAS. + let blob_exists = store.has(DigestInfo::try_from(wrong_blob_digest)?).await?; + assert_eq!(blob_exists, None); + assert_eq!( + cas_server + .chunking_metrics() + .splice_verification_failures + .load(Ordering::Relaxed), + 1 + ); + Ok(()) +} + +#[nativelink_test] +async fn splice_blob_rejects_size_mismatch() -> Result<(), Box> { + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + + let (chunk1_digest, chunk2_digest, blob_digest) = upload_test_chunks(&store).await?; + let wrong_blob_digest = Digest { + size_bytes: blob_digest.size_bytes + 1, + ..blob_digest + }; + + let status = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(wrong_blob_digest), + chunk_digests: vec![chunk1_digest, chunk2_digest], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(status.code(), Code::InvalidArgument); + assert!( + status + .message() + .contains("does not match the expected blob size"), + "unexpected message: {}", + status.message() + ); + Ok(()) +} + +#[nativelink_test] +async fn splice_blob_missing_chunk_returns_not_found() -> Result<(), Box> { + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + + // Only upload the first chunk. + let chunk1_digest = Digest { + hash: HASH1.to_string(), + size_bytes: CHUNK1_VALUE.len() as i64, + }; + store + .update_oneshot( + DigestInfo::try_from(chunk1_digest.clone())?, + CHUNK1_VALUE.into(), + ) + .await?; + let missing_chunk_digest = Digest { + hash: HASH2.to_string(), + size_bytes: CHUNK2_VALUE.len() as i64, + }; + let mut hasher = DigestHasherFunc::Sha256.hasher(); + hasher.update(CHUNK1_VALUE.as_bytes()); + hasher.update(CHUNK2_VALUE.as_bytes()); + let blob_digest: Digest = hasher.finalize_digest().into(); + + let status = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest), + chunk_digests: vec![chunk1_digest, missing_chunk_digest], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(status.code(), Code::NotFound); + Ok(()) +} + +#[nativelink_test] +async fn split_blob_absent_blob_returns_not_found() -> Result<(), Box> { + const VALUE: &str = "1"; + + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + + // The blob was never uploaded. + let status = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(Digest { + hash: HASH1.to_string(), + size_bytes: VALUE.len() as i64, + }), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(status.code(), Code::NotFound); + let metrics = cas_server.chunking_metrics(); + assert_eq!(metrics.split_requests_total.load(Ordering::Relaxed), 1); + assert_eq!(metrics.split_hits.load(Ordering::Relaxed), 0); + assert_eq!(metrics.split_misses.load(Ordering::Relaxed), 1); + Ok(()) +} + +#[nativelink_test] +async fn split_and_splice_disabled_return_unimplemented() -> Result<(), Box> +{ + const VALUE: &str = "1"; + + let store_manager = make_store_manager().await?; + let cas_server = make_cas_server(&store_manager)?; + + let digest = Digest { + hash: HASH1.to_string(), + size_bytes: VALUE.len() as i64, + }; + let split_status = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(digest.clone()), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(split_status.code(), Code::Unimplemented); + + let splice_status = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(digest.clone()), + chunk_digests: vec![digest], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(splice_status.code(), Code::Unimplemented); + Ok(()) +} + +#[nativelink_test] +async fn split_blob_chunks_small_blob_on_demand() -> Result<(), Box> { + const VALUE: &str = "1"; + + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + + // Upload the blob whole (as a remote execution worker would) under its + // real digest, without ever calling SpliceBlob. + let mut hasher = DigestHasherFunc::Sha256.hasher(); + hasher.update(VALUE.as_bytes()); + let blob_digest: Digest = hasher.finalize_digest().into(); + store + .update_oneshot(DigestInfo::try_from(blob_digest.clone())?, VALUE.into()) + .await?; + + let split_response = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest.clone()), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + // A blob smaller than the minimum chunk size is a single chunk whose + // digest equals the blob digest. + assert_eq!(split_response.chunk_digests, vec![blob_digest]); + assert_eq!( + split_response.chunking_function, + i32::from(chunking_function::Value::FastCdc2020) + ); + let metrics = cas_server.chunking_metrics(); + assert_eq!(metrics.split_chunked_on_demand.load(Ordering::Relaxed), 1); + assert_eq!(metrics.split_hits.load(Ordering::Relaxed), 0); + Ok(()) +} + +#[nativelink_test] +async fn split_blob_chunks_large_blob_on_demand_and_reuses_layout() +-> Result<(), Box> { + // Use the smallest allowed average (1 KiB -> min 256, max 4096) so a + // small test blob still produces multiple chunks. + const AVG_CHUNK_SIZE: u64 = 1024; + const BLOB_SIZE: usize = 16 * 1024; + + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server_with_avg(&store_manager, AVG_CHUNK_SIZE)?; + let store = store_manager.get_store("main_cas").unwrap(); + + // Deterministic pseudo-random content so FastCDC finds content-defined + // boundaries. + let mut state = 0x9e37_79b9_u32; + let data: Vec = (0..BLOB_SIZE) + .map(|_| { + state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223); + (state >> 24) as u8 + }) + .collect(); + let blob_digest = Digest { + hash: HASH1.to_string(), + size_bytes: BLOB_SIZE as i64, + }; + store + .update_oneshot( + DigestInfo::try_from(blob_digest.clone())?, + bytes::Bytes::from(data.clone()), + ) + .await?; + + let split_response = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest.clone()), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert!( + split_response.chunk_digests.len() > 1, + "expected multiple chunks, got {}", + split_response.chunk_digests.len() + ); + + // All chunks must be stored in the CAS and concatenate to the original + // blob in order. + let mut reassembled = Vec::with_capacity(BLOB_SIZE); + for chunk_digest in &split_response.chunk_digests { + let chunk_data = store + .get_part_unchunked(DigestInfo::try_from(chunk_digest.clone())?, 0, None) + .await?; + reassembled.extend_from_slice(&chunk_data); + } + assert_eq!(reassembled, data); + + // A second split must be served from the stored layout. + let second_response = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(second_response.chunk_digests, split_response.chunk_digests); + let metrics = cas_server.chunking_metrics(); + assert_eq!(metrics.split_requests_total.load(Ordering::Relaxed), 2); + assert_eq!(metrics.split_chunked_on_demand.load(Ordering::Relaxed), 1); + assert_eq!(metrics.split_hits.load(Ordering::Relaxed), 1); + Ok(()) +} + +#[nativelink_test] +async fn split_blob_falls_back_when_layout_unusable() -> Result<(), Box> { + const VALUE: &str = "1"; + + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + let index_store = store_manager.get_store("chunk_index").unwrap(); + + let mut hasher = DigestHasherFunc::Sha256.hasher(); + hasher.update(VALUE.as_bytes()); + let blob_digest: Digest = hasher.finalize_digest().into(); + store + .update_oneshot(DigestInfo::try_from(blob_digest.clone())?, VALUE.into()) + .await?; + + // Register a layout whose only chunk is not present in the CAS, + // simulating a chunk that was evicted after the layout was stored. + let stale_layout = SplitBlobResponse { + chunk_digests: vec![Digest { + hash: HASH2.to_string(), + size_bytes: VALUE.len() as i64, + }], + chunking_function: chunking_function::Value::FastCdc2020.into(), + }; + index_store + .update_oneshot( + DigestInfo::try_from(blob_digest.clone())?, + stale_layout.encode_to_vec().into(), + ) + .await?; + + // The unusable layout must be ignored and the blob re-chunked on demand. + let split_response = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest.clone()), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(split_response.chunk_digests, vec![blob_digest]); + let metrics = cas_server.chunking_metrics(); + assert_eq!(metrics.split_hits.load(Ordering::Relaxed), 0); + assert_eq!(metrics.split_chunked_on_demand.load(Ordering::Relaxed), 1); + Ok(()) +} + +#[nativelink_test] +async fn chunking_rejects_index_store_same_as_cas_store() -> Result<(), Box> +{ + let store_manager = make_store_manager().await?; + let error = CasServer::new( + &[WithInstanceName { + instance_name: INSTANCE_NAME.to_string(), + config: nativelink_config::cas_server::CasStoreConfig { + cas_store: "main_cas".to_string(), + experimental_chunking: Some(nativelink_config::cas_server::CasChunkingConfig { + index_store: Some("main_cas".to_string()), + avg_chunk_size_bytes: 0, + max_chunk_count: 0, + }), + }, + }], + &store_manager, + ) + .err() + .expect("expected same-store index_store to be rejected"); + assert!( + error + .to_string() + .contains("must not be the same store as 'cas_store'"), + "unexpected error: {error}" + ); + Ok(()) +} + +#[nativelink_test] +async fn chunking_on_grpc_store_forbids_index_store() -> Result<(), Box> { + let store_manager = Arc::new(StoreManager::new()); + store_manager.add_store( + "grpc_cas", + store_factory( + &StoreSpec::Grpc(nativelink_config::stores::GrpcSpec { + instance_name: "backend".to_string(), + endpoints: vec![nativelink_config::stores::GrpcEndpoint { + address: "http://localhost:1".to_string(), + tls_config: None, + concurrency_limit: None, + connect_timeout_s: 0, + tcp_keepalive_s: 0, + http2_keepalive_interval_s: 0, + http2_keepalive_timeout_s: 0, + }], + store_type: nativelink_config::stores::StoreType::Cas, + retry: nativelink_config::stores::Retry::default(), + max_concurrent_requests: 0, + connections_per_endpoint: 0, + rpc_timeout_s: 1, + use_legacy_resource_names: false, + headers: std::collections::HashMap::new(), + forward_headers: vec![], + }), + &store_manager, + None, + ) + .await?, + ); + + let make_config = |index_store: Option| { + vec![WithInstanceName { + instance_name: INSTANCE_NAME.to_string(), + config: nativelink_config::cas_server::CasStoreConfig { + cas_store: "grpc_cas".to_string(), + experimental_chunking: Some(nativelink_config::cas_server::CasChunkingConfig { + index_store, + avg_chunk_size_bytes: 0, + max_chunk_count: 0, + }), + }, + }] + }; + + // A local index store is meaningless when the RPCs are forwarded. + let error = CasServer::new(&make_config(Some("grpc_cas".to_string())), &store_manager) + .err() + .expect("expected index_store on grpc store to be rejected"); + assert!( + error.to_string().contains("must not be set"), + "unexpected error: {error}" + ); + + // Without an index_store the configuration is valid: SplitBlob and + // SpliceBlob are forwarded to the backend. + CasServer::new(&make_config(None), &store_manager)?; + Ok(()) +} + +#[nativelink_test] +async fn max_chunk_count_limits_split_and_splice() -> Result<(), Box> { + // avg 1024 (min allowed) with max_chunk_count 2: the 16 KiB test blob + // chunks to more than 2 pieces, so on-demand splitting must refuse. + const AVG_CHUNK_SIZE: u64 = 1024; + const BLOB_SIZE: usize = 16 * 1024; + + let store_manager = make_chunking_store_manager().await?; + let cas_server = CasServer::new( + &[WithInstanceName { + instance_name: INSTANCE_NAME.to_string(), + config: nativelink_config::cas_server::CasStoreConfig { + cas_store: "main_cas".to_string(), + experimental_chunking: Some(nativelink_config::cas_server::CasChunkingConfig { + index_store: Some("chunk_index".to_string()), + avg_chunk_size_bytes: AVG_CHUNK_SIZE, + max_chunk_count: 2, + }), + }, + }], + &store_manager, + )?; + let store = store_manager.get_store("main_cas").unwrap(); + + let mut state = 0x9e37_79b9_u32; + let data: Vec = (0..BLOB_SIZE) + .map(|_| { + state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223); + (state >> 24) as u8 + }) + .collect(); + let blob_digest = Digest { + hash: HASH1.to_string(), + size_bytes: BLOB_SIZE as i64, + }; + store + .update_oneshot( + DigestInfo::try_from(blob_digest.clone())?, + bytes::Bytes::from(data), + ) + .await?; + + let status = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest.clone()), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(status.code(), Code::NotFound); + assert!( + status.message().contains("max_chunk_count"), + "unexpected message: {}", + status.message() + ); + + // Splices above the cap are rejected outright. + let chunk_digest = Digest { + hash: HASH2.to_string(), + size_bytes: 1, + }; + let status = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest), + chunk_digests: vec![chunk_digest.clone(), chunk_digest.clone(), chunk_digest], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await + .unwrap_err(); + assert_eq!(status.code(), Code::InvalidArgument); + assert!( + status.message().contains("expected at most 2"), + "unexpected message: {}", + status.message() + ); + Ok(()) +} + +// Bazel 9.1.1 with --digest_function=blake3 leaves digest_function unset in +// SplitBlob/SpliceBlob requests, which is length-ambiguous (SHA256 and +// BLAKE3 are both 32 bytes). The server must infer the function instead of +// assuming the default. +#[nativelink_test] +async fn chunking_infers_blake3_when_digest_function_unset() +-> Result<(), Box> { + const VALUE: &str = "blake3 blob content"; + + let store_manager = make_chunking_store_manager().await?; + let cas_server = make_chunking_cas_server(&store_manager)?; + let store = store_manager.get_store("main_cas").unwrap(); + + let mut hasher = DigestHasherFunc::Blake3.hasher(); + hasher.update(VALUE.as_bytes()); + let blob_digest: Digest = hasher.finalize_digest().into(); + + // Splice: the single chunk is the blob itself, uploaded under its + // BLAKE3 digest, with digest_function left unset. + store + .update_oneshot(DigestInfo::try_from(blob_digest.clone())?, VALUE.into()) + .await?; + let splice_response = cas_server + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(blob_digest.clone()), + chunk_digests: vec![blob_digest.clone()], + digest_function: 0, + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(splice_response.blob_digest.as_ref(), Some(&blob_digest)); + + // On-demand split of a fresh blob uploaded whole: the returned chunk + // digests must be BLAKE3 (here a single chunk equal to the blob). + let mut hasher = DigestHasherFunc::Blake3.hasher(); + hasher.update(b"other blake3 content"); + let other_digest: Digest = hasher.finalize_digest().into(); + store + .update_oneshot( + DigestInfo::try_from(other_digest.clone())?, + bytes::Bytes::from_static(b"other blake3 content"), + ) + .await?; + let split_response = cas_server + .split_blob(Request::new(SplitBlobRequest { + instance_name: INSTANCE_NAME.to_string(), + blob_digest: Some(other_digest.clone()), + digest_function: 0, + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(split_response.chunk_digests, vec![other_digest]); + Ok(()) +} diff --git a/nativelink-service/tests/data/fastcdc2020_test_vectors.txt b/nativelink-service/tests/data/fastcdc2020_test_vectors.txt new file mode 100644 index 000000000..db89efd7d --- /dev/null +++ b/nativelink-service/tests/data/fastcdc2020_test_vectors.txt @@ -0,0 +1,35 @@ +# Test vectors for the FastCDC 2020 content-defined chunking algorithm +# +# Reference implementations: +# - Rust: https://github.com/nlfiedler/fastcdc-rs +# - Go: https://github.com/buildbuddy-io/fastcdc2020 +# +# Test input: +# Image: https://github.com/nlfiedler/fastcdc-rs/blob/49c3d0b/test/fixtures/SekienAkashita.jpg +# SHA256: d9e749d9367fc908876749d6502eb212fee88c9a94892fb07da5ef3ba8bc39ed +# Size: 109466 bytes +# +# Parameters: +# MinSize: 4096 +# AvgSize: 16384 (must be power of 2) +# MaxSize: 65535 +# Normalization: 2 +# +# Format: offset, length, sha256, fingerprint +# The fingerprint is the 64-bit gear hash value at the chunk boundary. + +# Seed: 0 +0 19186 0f9efa589121d5d9e9e2c4ace91337d77cae866537143f6f15a0ffd525a77c2d 17583755766661134474 +19186 19279 c7c86a165573c16448cda35c9169742e85645af42be22889f8b96b8ee0ec7cb0 4098594969649699419 +38465 17354 bc88521e28a8b4479cdea5f75aa721a24f3a0a7d0be903aa6d505c574e51e89d 2365586132076908760 +55819 16387 4b8dac2652e4685c629d2bb1ae9d4448e676b86f2e67ca0b2fff3d9580184b79 16009206469796846404 +72206 19940 c0a7062da6f2386c28e086ee0cedd5732252741269838773cff1ddb05b2df6ed 2473608525189754172 +92146 17320 7fa5b12134dc75cd2ac8dc60d3a8f3c8d22f0ee9d4cf74a4aa937e2a0d2d79a5 2504464741100432583 + +# Seed: 666 +0 17635 cb3a9d80a3569772d4ed331ca37ab0c862c759897b890fc1aac90a4f2ea3a407 17021115692437263050 +17635 17334 d758c6b7b0b7eef1e996f8ccd17de6c645360b03a26c35541e7581348ac08944 8231525949846907466 +34969 19136 24846aefd89e510594bae3e9d7d5ea5012067601512610fed126a3c57ba993f5 10944310959829698982 +54105 17467 efa785e1fefb49f190e665f72fd246c1442079874508c312196da1fb3040d00b 13602876513398592944 +71572 23593 a2f557bdd8d40d8faada963ad5f91ec54b10ccee7c5ae72754a65137592dc607 2945079350535657389 +95165 14301 e131100b4a7147ccad19dc63c4a2fac1f5d8b644e1373eeb6803825024234efc 8981594897574481255 diff --git a/nativelink-service/tests/fastcdc_conformance_test.rs b/nativelink-service/tests/fastcdc_conformance_test.rs new file mode 100644 index 000000000..5d13c5820 --- /dev/null +++ b/nativelink-service/tests/fastcdc_conformance_test.rs @@ -0,0 +1,152 @@ +// Copyright 2024 The NativeLink Authors. All rights reserved. +// +// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// See LICENSE file for details +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Conformance tests for the `FastCDC` 2020 implementation used by the +//! `SplitBlob` on-demand chunking path against the official REAPI test +//! vectors from the remote-apis repository: +//! +//! +//! The chunk boundaries produced by the server MUST match these vectors +//! byte-for-byte; otherwise chunks produced by clients (e.g. Bazel with +//! `--experimental_remote_cache_chunking`) never deduplicate against chunks +//! produced by the server and the feature silently loses its value. + +use fastcdc::v2020::{AsyncStreamCDC, FastCDC, Normalization}; +use futures::StreamExt; +use nativelink_macro::nativelink_test; +use pretty_assertions::assert_eq; +use sha2::{Digest as _, Sha256}; + +/// The canonical test input named in the vectors file header +/// (SHA256 d9e749d9367fc908876749d6502eb212fee88c9a94892fb07da5ef3ba8bc39ed), +/// shared with the existing `DedupStore` `FastCDC` test fixture. +const TEST_INPUT: &[u8] = include_bytes!("../../nativelink-util/tests/data/SekienAkashita.jpg"); +const TEST_VECTORS: &str = include_str!("data/fastcdc2020_test_vectors.txt"); + +/// Parameters stated in the vectors file header. +const MIN_SIZE: u32 = 4096; +const AVG_SIZE: u32 = 16384; +const MAX_SIZE: u32 = 65535; + +struct ExpectedChunk { + offset: u64, + length: usize, + sha256_hex: String, + fingerprint: u64, +} + +/// Parses the `# Seed: ` sections of the vectors file into +/// (seed, expected chunks) pairs. +fn parse_test_vectors() -> Vec<(u64, Vec)> { + let mut sections = Vec::new(); + for line in TEST_VECTORS.lines() { + let line = line.trim(); + if let Some(seed) = line.strip_prefix("# Seed: ") { + sections.push((seed.parse::().unwrap(), Vec::new())); + continue; + } + if line.is_empty() || line.starts_with('#') { + continue; + } + let mut fields = line.split_whitespace(); + let chunk = ExpectedChunk { + offset: fields.next().unwrap().parse().unwrap(), + length: fields.next().unwrap().parse().unwrap(), + sha256_hex: fields.next().unwrap().to_string(), + fingerprint: fields.next().unwrap().parse().unwrap(), + }; + sections + .last_mut() + .expect("chunk line before any '# Seed:' section") + .1 + .push(chunk); + } + assert!(!sections.is_empty(), "no seed sections parsed"); + sections +} + +#[nativelink_test] +async fn fastcdc2020_matches_reapi_test_vectors() -> Result<(), Box> { + assert_eq!( + hex::encode(Sha256::digest(TEST_INPUT)), + "d9e749d9367fc908876749d6502eb212fee88c9a94892fb07da5ef3ba8bc39ed", + "test fixture does not match the input named in the vectors file" + ); + + for (seed, expected_chunks) in parse_test_vectors() { + let chunks: Vec<_> = FastCDC::with_level_and_seed( + TEST_INPUT, + MIN_SIZE, + AVG_SIZE, + MAX_SIZE, + Normalization::Level2, + seed, + ) + .collect(); + assert_eq!( + chunks.len(), + expected_chunks.len(), + "chunk count mismatch for seed {seed}" + ); + for (chunk, expected) in chunks.iter().zip(&expected_chunks) { + assert_eq!(chunk.offset as u64, expected.offset, "offset, seed {seed}"); + assert_eq!(chunk.length, expected.length, "length, seed {seed}"); + assert_eq!(chunk.hash, expected.fingerprint, "fingerprint, seed {seed}"); + let data = &TEST_INPUT[chunk.offset..chunk.offset + chunk.length]; + assert_eq!( + hex::encode(Sha256::digest(data)), + expected.sha256_hex, + "chunk content sha256, seed {seed}" + ); + } + } + Ok(()) +} + +/// The streaming chunker (the variant `SplitBlob` actually uses) must +/// produce the same boundaries as the in-memory reference. +#[nativelink_test] +async fn fastcdc2020_streaming_matches_reapi_test_vectors() +-> Result<(), Box> { + let sections = parse_test_vectors(); + let (_, expected_chunks) = sections + .iter() + .find(|(seed, _)| *seed == 0) + .expect("seed 0 section missing"); + + let mut cdc = AsyncStreamCDC::with_level( + TEST_INPUT, + MIN_SIZE, + AVG_SIZE, + MAX_SIZE, + Normalization::Level2, + ); + let stream = cdc.as_stream(); + let mut stream = core::pin::pin!(stream); + let mut chunks = Vec::new(); + while let Some(chunk) = stream.next().await { + chunks.push(chunk.expect("chunking the test input failed")); + } + assert_eq!(chunks.len(), expected_chunks.len()); + for (chunk, expected) in chunks.iter().zip(expected_chunks) { + assert_eq!(chunk.offset, expected.offset); + assert_eq!(chunk.length, expected.length); + assert_eq!(chunk.hash, expected.fingerprint); + assert_eq!( + hex::encode(Sha256::digest(&chunk.data)), + expected.sha256_hex + ); + } + Ok(()) +} diff --git a/nativelink-store/src/grpc_store.rs b/nativelink-store/src/grpc_store.rs index 40edc4dc0..79d866179 100644 --- a/nativelink-store/src/grpc_store.rs +++ b/nativelink-store/src/grpc_store.rs @@ -29,7 +29,8 @@ use nativelink_proto::build::bazel::remote::execution::v2::content_addressable_s use nativelink_proto::build::bazel::remote::execution::v2::{ ActionResult, BatchReadBlobsRequest, BatchReadBlobsResponse, BatchUpdateBlobsRequest, BatchUpdateBlobsResponse, FindMissingBlobsRequest, FindMissingBlobsResponse, - GetActionResultRequest, GetTreeRequest, GetTreeResponse, UpdateActionResultRequest, + GetActionResultRequest, GetTreeRequest, GetTreeResponse, SpliceBlobRequest, SpliceBlobResponse, + SplitBlobRequest, SplitBlobResponse, UpdateActionResultRequest, }; use nativelink_proto::google::bytestream::byte_stream_client::ByteStreamClient; use nativelink_proto::google::bytestream::{ @@ -335,6 +336,64 @@ impl GrpcStore { .await } + pub async fn split_blob( + &self, + grpc_request: Request, + ) -> Result, Error> { + error_if!( + matches!(self.store_type, nativelink_config::stores::StoreType::Ac), + "CAS operation on AC store" + ); + + let mut request = grpc_request.into_inner(); + request.instance_name.clone_from(&self.instance_name); + self.perform_request(request, |request| async move { + let channel = self + .connection_manager + .connection(format!("split_blob: {:?}", request.blob_digest)) + .await + .err_tip(|| "in split_blob")?; + ContentAddressableStorageClient::new(channel) + .split_blob(enrich_request( + Request::new(request), + &self.headers, + &self.forward_headers, + )) + .await + .err_tip(|| "in GrpcStore::split_blob") + }) + .await + } + + pub async fn splice_blob( + &self, + grpc_request: Request, + ) -> Result, Error> { + error_if!( + matches!(self.store_type, nativelink_config::stores::StoreType::Ac), + "CAS operation on AC store" + ); + + let mut request = grpc_request.into_inner(); + request.instance_name.clone_from(&self.instance_name); + self.perform_request(request, |request| async move { + let channel = self + .connection_manager + .connection(format!("splice_blob: {:?}", request.blob_digest)) + .await + .err_tip(|| "in splice_blob")?; + ContentAddressableStorageClient::new(channel) + .splice_blob(enrich_request( + Request::new(request), + &self.headers, + &self.forward_headers, + )) + .await + .err_tip(|| "in GrpcStore::splice_blob") + }) + .await + } + fn get_read_request(&self, mut request: ReadRequest) -> Result { const IS_UPLOAD_FALSE: bool = false; let mut resource_info = ResourceInfo::new(&request.resource_name, IS_UPLOAD_FALSE)?; diff --git a/nativelink-store/src/verify_store.rs b/nativelink-store/src/verify_store.rs index e3722ff48..1b313e08e 100644 --- a/nativelink-store/src/verify_store.rs +++ b/nativelink-store/src/verify_store.rs @@ -23,13 +23,12 @@ use nativelink_util::buf_channel::{ DropCloserReadHalf, DropCloserWriteHalf, make_buf_channel_pair, }; use nativelink_util::common::PackedHash; -use nativelink_util::digest_hasher::{DigestHasher, DigestHasherFunc, default_digest_hasher_func}; +use nativelink_util::digest_hasher::{DigestHasher, digest_hasher_func_from_context}; use nativelink_util::health_utils::{HealthStatusIndicator, default_health_status_indicator}; use nativelink_util::metrics_utils::CounterWithTime; use nativelink_util::store_trait::{ RemoveItemCallback, Store, StoreDriver, StoreKey, StoreLike, UploadSizeInfo, }; -use opentelemetry::context::Context; #[derive(Debug, MetricsComponent)] pub struct VerifyStore { @@ -184,12 +183,7 @@ impl StoreDriver for VerifyStore { } let mut hasher = if self.verify_hash { - Some( - Context::current() - .get::() - .map_or_else(default_digest_hasher_func, |v| *v) - .hasher(), - ) + Some(digest_hasher_func_from_context().hasher()) } else { None }; diff --git a/nativelink-store/tests/grpc_store_test.rs b/nativelink-store/tests/grpc_store_test.rs index 3466997b2..858d5a653 100644 --- a/nativelink-store/tests/grpc_store_test.rs +++ b/nativelink-store/tests/grpc_store_test.rs @@ -9,8 +9,14 @@ use futures::{Stream, StreamExt}; use nativelink_config::stores::{GrpcEndpoint, GrpcSpec, Retry, StoreType}; use nativelink_error::{Error, ResultExt}; use nativelink_macro::nativelink_test; +use nativelink_proto::build::bazel::remote::execution::v2::content_addressable_storage_server::{ + ContentAddressableStorage, ContentAddressableStorageServer, +}; use nativelink_proto::build::bazel::remote::execution::v2::{ - FindMissingBlobsRequest, digest_function, + BatchReadBlobsRequest, BatchReadBlobsResponse, BatchUpdateBlobsRequest, + BatchUpdateBlobsResponse, Digest, FindMissingBlobsRequest, FindMissingBlobsResponse, + GetTreeRequest, GetTreeResponse, SpliceBlobRequest, SpliceBlobResponse, SplitBlobRequest, + SplitBlobResponse, chunking_function, digest_function, }; use nativelink_proto::google::bytestream::byte_stream_server::{ByteStream, ByteStreamServer}; use nativelink_proto::google::bytestream::{ @@ -322,3 +328,145 @@ async fn read_works_with_headers() -> Result<(), Error> { Ok(()) } + +#[derive(Debug, Clone)] +struct FakeCasServer { + split_requests: Arc>>, + splice_requests: Arc>>, +} + +impl FakeCasServer { + fn new() -> Self { + Self { + split_requests: Arc::new(Mutex::new(vec![])), + splice_requests: Arc::new(Mutex::new(vec![])), + } + } +} + +type GetTreeStream = Pin> + Send + 'static>>; + +#[tonic::async_trait] +impl ContentAddressableStorage for FakeCasServer { + type GetTreeStream = GetTreeStream; + + #[allow(clippy::unimplemented)] + async fn find_missing_blobs( + &self, + _grpc_request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + #[allow(clippy::unimplemented)] + async fn batch_update_blobs( + &self, + _grpc_request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + #[allow(clippy::unimplemented)] + async fn batch_read_blobs( + &self, + _grpc_request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + #[allow(clippy::unimplemented)] + async fn get_tree( + &self, + _grpc_request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + async fn split_blob( + &self, + grpc_request: Request, + ) -> Result, Status> { + let request = grpc_request.into_inner(); + self.split_requests.lock().await.push(request.clone()); + Ok(Response::new(SplitBlobResponse { + chunk_digests: request.blob_digest.into_iter().collect(), + chunking_function: request.chunking_function, + })) + } + + async fn splice_blob( + &self, + grpc_request: Request, + ) -> Result, Status> { + let request = grpc_request.into_inner(); + self.splice_requests.lock().await.push(request.clone()); + Ok(Response::new(SpliceBlobResponse { + blob_digest: request.blob_digest, + })) + } +} + +async fn make_fake_cas_server() -> (FakeCasServer, u16) { + let fake_cas_server = FakeCasServer::new(); + let server = ContentAddressableStorageServer::new(fake_cas_server.clone()); + let listener = TcpIncoming::bind("127.0.0.1:0".parse().unwrap()).unwrap(); + let port = listener.local_addr().unwrap().port(); + + background_spawn!("server", async move { + Server::builder() + .add_service(server) + .serve_with_incoming(listener) + .await + .unwrap(); + }); + + (fake_cas_server, port) +} + +#[nativelink_test] +async fn split_and_splice_blob_forward_to_backend() -> Result<(), Error> { + let (server, port) = make_fake_cas_server().await; + let mut spec = test_spec(format!("http://localhost:{port}"), false); + spec.instance_name = "backend_instance".to_string(); + let store = GrpcStore::new(&spec).await?; + + let digest = Digest { + hash: VALID_HASH.to_string(), + size_bytes: RAW_INPUT.len() as i64, + }; + + let split_response = store + .split_blob(Request::new(SplitBlobRequest { + instance_name: "local_instance".to_string(), + blob_digest: Some(digest.clone()), + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(split_response.chunk_digests, vec![digest.clone()]); + { + let split_requests = server.split_requests.lock().await; + assert_eq!(split_requests.len(), 1); + // The instance name must be rewritten to the backend's. + assert_eq!(split_requests[0].instance_name, "backend_instance"); + } + + let splice_response = store + .splice_blob(Request::new(SpliceBlobRequest { + instance_name: "local_instance".to_string(), + blob_digest: Some(digest.clone()), + chunk_digests: vec![digest.clone()], + digest_function: digest_function::Value::Sha256.into(), + chunking_function: chunking_function::Value::FastCdc2020.into(), + })) + .await? + .into_inner(); + assert_eq!(splice_response.blob_digest, Some(digest)); + { + let splice_requests = server.splice_requests.lock().await; + assert_eq!(splice_requests.len(), 1); + assert_eq!(splice_requests[0].instance_name, "backend_instance"); + } + Ok(()) +} diff --git a/nativelink-util/BUILD.bazel b/nativelink-util/BUILD.bazel index ec442cdcb..f9e0dcd0a 100644 --- a/nativelink-util/BUILD.bazel +++ b/nativelink-util/BUILD.bazel @@ -7,6 +7,9 @@ load( "rust_test_suite", ) +# Shared with the FastCDC conformance test in nativelink-service. +exports_files(["tests/data/SekienAkashita.jpg"]) + rust_library( name = "nativelink-util", srcs = [ diff --git a/nativelink-util/src/digest_hasher.rs b/nativelink-util/src/digest_hasher.rs index 51c911c63..e0a37b0af 100644 --- a/nativelink-util/src/digest_hasher.rs +++ b/nativelink-util/src/digest_hasher.rs @@ -53,6 +53,14 @@ pub fn default_digest_hasher_func() -> DigestHasherFunc { *DEFAULT_DIGEST_HASHER_FUNC.get_or_init(|| DigestHasherFunc::Sha256) } +/// Get the hasher requested by the client from the active context (set via +/// [`make_ctx_for_hash_func`]), falling back to the default hasher. +pub fn digest_hasher_func_from_context() -> DigestHasherFunc { + Context::current() + .get::() + .map_or_else(default_digest_hasher_func, |v| *v) +} + /// Sets the default hasher to use if no hasher was requested by the client. pub fn set_default_digest_hasher_func(hasher: DigestHasherFunc) -> Result<(), Error> { DEFAULT_DIGEST_HASHER_FUNC diff --git a/run_integration_tests.sh b/run_integration_tests.sh index c1009a909..451cf1a27 100755 --- a/run_integration_tests.sh +++ b/run_integration_tests.sh @@ -127,7 +127,10 @@ for pattern in "${TEST_PATTERNS[@]}"; do bazel --output_base="$BAZEL_CACHE_DIR" clean FILENAME=$(basename "$fullpath") echo "Running test $FILENAME" - sudo env RUST_LOG=info docker compose up -d + # sudo resets the environment, so NATIVELINK_DIR must be passed + # through explicitly or docker compose falls back to mounting + # root's ~/.cache/nativelink instead of the per-run cache dir. + sudo env RUST_LOG=info NATIVELINK_DIR="$NATIVELINK_DIR" docker compose up -d if perl -e 'alarm shift; exec @ARGV' 30 bash -c 'until sudo docker compose logs | grep -q "Ready, listening on"; do sleep 1; done'; then echo "String 'Ready, listening on' found in the logs." else diff --git a/src/bin/nativelink.rs b/src/bin/nativelink.rs index 33e1c6ad5..4137940c2 100644 --- a/src/bin/nativelink.rs +++ b/src/bin/nativelink.rs @@ -30,8 +30,8 @@ use hyper_util::server::conn::auto; use hyper_util::service::TowerToHyperService; use mimalloc::MiMalloc; use nativelink_config::cas_server::{ - CasConfig, GlobalConfig, HttpCompressionAlgorithm, ListenerConfig, SchedulerConfig, - ServerConfig, StoreConfig, WorkerConfig, + CasConfig, CasStoreConfig, GlobalConfig, HttpCompressionAlgorithm, ListenerConfig, + SchedulerConfig, ServerConfig, StoreConfig, WithInstanceName, WorkerConfig, }; use nativelink_config::stores::ConfigDigestHashFunction; use nativelink_error::{Code, Error, ResultExt, make_err, make_input_err}; @@ -263,6 +263,17 @@ async fn inner_main( let server_cfgs: Vec = cfg.servers.into_iter().collect(); + // The capabilities service advertises chunking support for CAS instances + // that may be served from a different server block (e.g. behind an L7 + // router), so collect the CAS configs across all blocks. + let all_cas_configs: Vec> = server_cfgs + .iter() + .filter_map(|server_cfg| server_cfg.services.as_ref()) + .filter_map(|services| services.cas.as_deref()) + .flatten() + .cloned() + .collect(); + for server_cfg in server_cfgs { let services = server_cfg .services @@ -292,8 +303,9 @@ async fn inner_main( .add_optional_service( services .cas + .as_deref() .map_or(Ok(None), |cfg| { - CasServer::new(&cfg, &store_manager) + CasServer::new(cfg, &store_manager) .map(|v| Some(service_setup!(v.into_service(), http_config))) }) .err_tip(|| "Could not create CAS service")?, @@ -335,10 +347,9 @@ async fn inner_main( ) .add_optional_service( OptionFuture::from( - services - .capabilities - .as_ref() - .map(|cfg| CapabilitiesServer::new(cfg, &action_schedulers)), + services.capabilities.as_ref().map(|cfg| { + CapabilitiesServer::new(cfg, &action_schedulers, &all_cas_configs) + }), ) .await .map_or(Ok::, Error>(None), |server| { diff --git a/web/apps/docs/content/docs/configuration/chunking.mdx b/web/apps/docs/content/docs/configuration/chunking.mdx new file mode 100644 index 000000000..605284ed7 --- /dev/null +++ b/web/apps/docs/content/docs/configuration/chunking.mdx @@ -0,0 +1,110 @@ +--- +title: Content-defined chunking +description: Cut remote cache transfer bytes by 80-90% for incrementally changing artifacts with the REAPI SplitBlob/SpliceBlob extension and Bazel's --experimental_remote_cache_chunking. +--- + +When a large build output changes slightly — a relinked binary, a container +layer with one file modified — its digest changes, and a conventional remote +cache re-transfers the whole blob. Content-defined chunking (CDC) splits +blobs into chunks at content-derived boundaries, so clients upload and +download only the chunks that actually changed. In our measurements a +one-file change to a 16.8 MB tar layer re-uploaded 2.1 MB (87.5% less), and +a one-source-file change to a linked binary re-uploaded 80.8% less. + +NativeLink implements the server side of the +[REAPI blob split/splice extension](https://github.com/bazelbuild/remote-apis/pull/282): +`SpliceBlob` re-assembles chunked uploads (verifying the digest before +committing anything), and `SplitBlob` serves chunk layouts for downloads — +chunking blobs on demand with FastCDC 2020 when they were uploaded whole, +which is what makes chunked downloads work for outputs produced by remote +execution workers. + +## Requirements + +- **Bazel 9.1.1+ or 8.7.0+** on the client, with + `--experimental_remote_cache_chunking`. Avoid 9.1.0: it has a client bug + that corrupts outputs when the chunking flag is combined with + `--disk_cache` (fixed in 9.1.1). +- Chunking is **optional and off by default**. Without the configuration + below, NativeLink behaves exactly as before and does not advertise + chunking support, so clients fall back to regular transfers. + +## Enabling it + +Add an `experimental_chunking` block to the CAS service and give it a small +store for chunk layouts. The index store must not verify content digests +and must not be the CAS store itself: + +```json5 +stores: [ + { + name: "CAS_MAIN_STORE", + // ... your existing CAS store ... + }, + { + // Blob-to-chunks layouts: roughly 80-140 bytes per chunk. + name: "CHUNK_INDEX_STORE", + filesystem: { + content_path: "/tmp/nativelink/data/content_path-chunk-index", + temp_path: "/tmp/nativelink/data/tmp_path-chunk-index", + eviction_policy: { max_bytes: 100000000 }, + }, + }, +], +servers: [ + { + // ... + services: { + cas: [ + { + cas_store: "CAS_MAIN_STORE", + experimental_chunking: { + index_store: "CHUNK_INDEX_STORE", + // Optional; the REAPI-recommended default. Must be between + // 1 KiB and 1 MiB. Blobs smaller than 4x this value are + // never chunked. + avg_chunk_size_bytes: 524288, + // Optional; blobs producing more chunks than this are served + // without chunking (~25 GiB at the default average). + max_chunk_count: 50000, + }, + }, + ], + // The capabilities service advertises chunking support; clients + // only use it when advertised. + capabilities: [{}], + // ... + }, + }, +], +``` + +A complete runnable example lives at +[`nativelink-config/examples/chunking_cas.json5`](https://github.com/TraceMachina/nativelink/blob/main/nativelink-config/examples/chunking_cas.json5). +For instances whose `cas_store` is a grpc proxy store, omit `index_store`: +the chunking RPCs are forwarded to the backend, which owns the layouts. + +Then build with: + +```sh +bazel build //... \ + --remote_cache=grpc://your-nativelink:50051 \ + --experimental_remote_cache_chunking +``` + +## When it helps, and when it doesn't + +Chunking pays off when clients reach the cache across a real network (WAN, +metered links, cross-region) and artifacts change incrementally: +uncompressed archives, linked binaries, and container layers typically save +80–90% of transfer bytes per change. It does little on same-rack links — +saved bytes only save time when the wire is the bottleneck — and little for +compressed artifacts, where everything after the first changed byte +re-transfers. Small blobs (below 4x the average chunk size) are never +chunked, so hot small-object traffic is unaffected. + +The server verifies every spliced blob's digest before committing it and +materializes the full blob, so non-chunking clients and every existing read +path see ordinary blobs. Storage grows by roughly the chunk bytes for +chunk-eligible blobs; pairing the CAS with a `dedup` or `compression` store +composes normally. diff --git a/web/apps/docs/content/docs/configuration/meta.json b/web/apps/docs/content/docs/configuration/meta.json index 3f037a30c..73c150728 100644 --- a/web/apps/docs/content/docs/configuration/meta.json +++ b/web/apps/docs/content/docs/configuration/meta.json @@ -2,7 +2,8 @@ "pages": [ "intro", "basic", - "production" + "production", + "chunking" ], "title": "Configuring NativeLink" } From 8162a935284d770626351c37d531f50b27fb223c Mon Sep 17 00:00:00 2001 From: Marcus Eagan Date: Tue, 7 Jul 2026 06:43:44 -0700 Subject: [PATCH 027/144] Disable Bazel lockfile in 8.7 compatibility lane (#2517) --- .github/workflows/native-bazel.yaml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/native-bazel.yaml b/.github/workflows/native-bazel.yaml index fc8270452..0ad0f3ca2 100644 --- a/.github/workflows/native-bazel.yaml +++ b/.github/workflows/native-bazel.yaml @@ -28,11 +28,9 @@ jobs: # Keep the existing required check names on the project-pinned Bazel. - os: ubuntu-24.04 bazel_version: 9.1.1 - lockfile_mode: error check_name: ubuntu-24.04 - os: macos-26 bazel_version: 9.1.1 - lockfile_mode: error check_name: macos-26 # Deterministic Bazel compatibility lane. Randomizing Bazel versions # makes failures harder to reproduce. Bazel 8 cannot read the @@ -40,7 +38,6 @@ jobs: # without enforcing the committed MODULE.bazel.lock. - os: ubuntu-24.04 bazel_version: 8.7.0 - lockfile_mode: "off" check_name: ubuntu-24.04 / Bazel 8.7.0 name: ${{ matrix.check_name }} runs-on: ${{ matrix.os }} @@ -69,13 +66,21 @@ jobs: - name: Run Bazel tests run: | if [ "$RUNNER_OS" == "Linux" ] || [ "$RUNNER_OS" == "macOS" ]; then - bazel test //... \ - --lockfile_mode=${{ matrix.lockfile_mode }} \ - --extra_toolchains=@rust_toolchains//:all \ - --verbose_failures + if [ "${{ matrix.bazel_version }}" == "8.7.0" ]; then + # Bazel 8 cannot parse the Bazel 9 MODULE.bazel.lock format. + bazel test //... \ + --lockfile_mode=off \ + --extra_toolchains=@rust_toolchains//:all \ + --verbose_failures + else + bazel test //... \ + --lockfile_mode=error \ + --extra_toolchains=@rust_toolchains//:all \ + --verbose_failures + fi elif [ "$RUNNER_OS" == "Windows" ]; then bazel \ - --lockfile_mode=${{ matrix.lockfile_mode }} \ + --lockfile_mode=error \ --output_user_root=${{ steps.bazel-cache.outputs.mountpoint }} \ test \ --config=windows \ From 0ac1434f1965bc53d0b28d1cbbfe15f2c4b81acc Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Tue, 7 Jul 2026 16:02:26 +0100 Subject: [PATCH 028/144] Upgrade crossbeam-epoch and serial_test (#2519) --- Cargo.lock | 31 ++++++++----------------------- MODULE.bazel.lock | 8 +++----- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9efaf70f5..5bb260893 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1258,9 +1258,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] @@ -4443,15 +4443,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd", -] - [[package]] name = "schannel" version = "0.1.28" @@ -4504,12 +4495,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sdd" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" - [[package]] name = "sec1" version = "0.7.3" @@ -4689,22 +4674,22 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.2.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" +checksum = "699f4197115b8a7e7ff19c9a315a4bd6fffec26cc4626ef45ecaea389e081c6d" dependencies = [ - "futures", + "futures-executor", + "futures-util", "once_cell", "parking_lot", - "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "3.2.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" +checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 89d6e6a81..2eb5cdba9 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -893,7 +893,7 @@ "crc_3.3.0": "{\"dependencies\":[{\"name\":\"crc-catalog\",\"req\":\"^2.4.0\"}],\"features\":{}}", "critical-section_1.2.0": "{\"dependencies\":[],\"features\":{\"restore-state-bool\":[],\"restore-state-none\":[],\"restore-state-u16\":[],\"restore-state-u32\":[],\"restore-state-u64\":[],\"restore-state-u8\":[],\"restore-state-usize\":[],\"std\":[\"restore-state-bool\"]}}", "crossbeam-channel_0.5.15": "{\"dependencies\":[{\"default_features\":false,\"name\":\"crossbeam-utils\",\"req\":\"^0.8.18\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.13.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"signal-hook\",\"req\":\"^0.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"crossbeam-utils/std\"]}}", - "crossbeam-epoch_0.9.18": "{\"dependencies\":[{\"default_features\":false,\"name\":\"crossbeam-utils\",\"req\":\"^0.8.18\"},{\"name\":\"loom-crate\",\"optional\":true,\"package\":\"loom\",\"req\":\"^0.7.1\",\"target\":\"cfg(crossbeam_loom)\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"loom\":[\"loom-crate\",\"crossbeam-utils/loom\"],\"nightly\":[\"crossbeam-utils/nightly\"],\"std\":[\"alloc\",\"crossbeam-utils/std\"]}}", + "crossbeam-epoch_0.9.20": "{\"dependencies\":[{\"default_features\":false,\"name\":\"crossbeam-utils\",\"req\":\"^0.8.18\"},{\"name\":\"loom-crate\",\"optional\":true,\"package\":\"loom\",\"req\":\"^0.7.1\",\"target\":\"cfg(crossbeam_loom)\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"loom\":[\"loom-crate\",\"crossbeam-utils/loom\"],\"nightly\":[\"crossbeam-utils/nightly\"],\"std\":[\"alloc\",\"crossbeam-utils/std\"]}}", "crossbeam-utils_0.8.21": "{\"dependencies\":[{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7.1\",\"target\":\"cfg(crossbeam_loom)\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"}],\"features\":{\"default\":[\"std\"],\"nightly\":[],\"std\":[]}}", "crunchy_0.2.4": "{\"dependencies\":[],\"features\":{\"default\":[\"limit_128\"],\"limit_1024\":[],\"limit_128\":[],\"limit_2048\":[],\"limit_256\":[],\"limit_512\":[],\"limit_64\":[],\"std\":[]}}", "crypto-bigint_0.5.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"der\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"generic-array\",\"optional\":true,\"req\":\"^0.14\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"num-bigint\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"num-integer\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand_chacha\",\"req\":\"^0.3\"},{\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6.4\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"default_features\":false,\"name\":\"rlp\",\"optional\":true,\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"serdect\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.4\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"serdect?/alloc\"],\"default\":[\"rand\"],\"extra-sizes\":[],\"rand\":[\"rand_core/std\"],\"serde\":[\"dep:serdect\"]}}", @@ -1194,7 +1194,6 @@ "rustversion_1.0.22": "{\"dependencies\":[{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", "ryu_1.0.23": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(miri))\"},{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.5\"}],\"features\":{\"small\":[]}}", "same-file_1.0.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"winapi-util\",\"req\":\"^0.1.1\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "scc_2.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"name\":\"sdd\",\"req\":\"^3.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.47\"}],\"features\":{\"loom\":[\"dep:loom\",\"sdd/loom\"]}}", "schannel_0.1.28": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_Security_Cryptography\",\"Win32_Security_Authentication_Identity\",\"Win32_Security_Credentials\",\"Win32_System_LibraryLoader\",\"Win32_System_Memory\",\"Win32_System_SystemInformation\"],\"name\":\"windows-sys\",\"req\":\"^0.61\"},{\"features\":[\"Win32_System_SystemInformation\",\"Win32_System_Time\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\"}],\"features\":{}}", "schemars_0.9.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec07\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"arrayvec07\",\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"bigdecimal04\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bigdecimal04\",\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"bytes1\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bytes1\",\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"chrono04\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono04\",\"package\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"either1\",\"optional\":true,\"package\":\"either\",\"req\":\"^1.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"either1\",\"package\":\"either\",\"req\":\"^1.3\"},{\"features\":[\"derive\",\"email\",\"regex\",\"url\"],\"kind\":\"dev\",\"name\":\"garde\",\"req\":\"^0.22\"},{\"default_features\":false,\"name\":\"indexmap2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"indexmap2\",\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"jiff02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"jiff02\",\"package\":\"jiff\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.30\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"ref-cast\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"},{\"default_features\":false,\"name\":\"rust_decimal1\",\"optional\":true,\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"rust_decimal1\",\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"name\":\"schemars_derive\",\"optional\":true,\"req\":\"=0.9.0\"},{\"default_features\":false,\"name\":\"semver1\",\"optional\":true,\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"semver1\",\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_json\",\"req\":\"^1.0.127\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"default_features\":false,\"name\":\"smallvec1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smallvec1\",\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"smol_str02\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str02\",\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.17\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"url2\",\"optional\":true,\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\",\"std\"],\"kind\":\"dev\",\"name\":\"url2\",\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"uuid1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"uuid1\",\"package\":\"uuid\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"validator\",\"req\":\"^0.20\"}],\"features\":{\"_ui_test\":[],\"default\":[\"derive\",\"std\"],\"derive\":[\"schemars_derive\"],\"preserve_order\":[\"serde_json/preserve_order\"],\"raw_value\":[\"serde_json/raw_value\"],\"std\":[]}}", "schemars_1.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec07\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"arrayvec07\",\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"bigdecimal04\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bigdecimal04\",\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"bytes1\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bytes1\",\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"chrono04\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4.39\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono04\",\"package\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0.17\"},{\"default_features\":false,\"name\":\"either1\",\"optional\":true,\"package\":\"either\",\"req\":\"^1.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"either1\",\"package\":\"either\",\"req\":\"^1.3\"},{\"features\":[\"derive\",\"email\",\"regex\",\"url\"],\"kind\":\"dev\",\"name\":\"garde\",\"req\":\"^0.22\"},{\"default_features\":false,\"name\":\"indexmap2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.2.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"indexmap2\",\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"jiff02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"jiff02\",\"package\":\"jiff\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.30\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"ref-cast\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"},{\"default_features\":false,\"name\":\"rust_decimal1\",\"optional\":true,\"package\":\"rust_decimal\",\"req\":\"^1.13\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"rust_decimal1\",\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"name\":\"schemars_derive\",\"optional\":true,\"req\":\"=1.2.1\"},{\"default_features\":false,\"name\":\"semver1\",\"optional\":true,\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"semver1\",\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_json\",\"req\":\"^1.0.127\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"default_features\":false,\"name\":\"smallvec1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smallvec1\",\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"smol_str02\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str02\",\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"smol_str03\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str03\",\"package\":\"smol_str\",\"req\":\"^0.3.2\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.17\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"url2\",\"optional\":true,\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\",\"std\"],\"kind\":\"dev\",\"name\":\"url2\",\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"uuid1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"uuid1\",\"package\":\"uuid\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"validator\",\"req\":\"^0.20\"}],\"features\":{\"_ui_test\":[],\"default\":[\"derive\",\"std\"],\"derive\":[\"schemars_derive\"],\"preserve_order\":[\"serde_json/preserve_order\"],\"raw_value\":[\"serde_json/raw_value\"],\"std\":[]}}", @@ -1202,7 +1201,6 @@ "scopeguard_1.2.0": "{\"dependencies\":[],\"features\":{\"default\":[\"use_std\"],\"use_std\":[]}}", "scroll_0.13.0": "{\"dependencies\":[{\"name\":\"scroll_derive\",\"optional\":true,\"req\":\"^0.13\"}],\"features\":{\"default\":[\"std\"],\"derive\":[\"dep:scroll_derive\"],\"std\":[]}}", "scroll_derive_0.13.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"scroll\",\"req\":\"^0.13\"},{\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", - "sdd_3.0.10": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.6\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"}],\"features\":{}}", "sec1_0.7.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"base16ct\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"oid\"],\"name\":\"der\",\"optional\":true,\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"generic-array\",\"optional\":true,\"req\":\"^0.14.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"pkcs8\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serdect\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"subtle\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"der?/alloc\",\"pkcs8?/alloc\",\"zeroize?/alloc\"],\"default\":[\"der\",\"point\"],\"der\":[\"dep:der\",\"zeroize\"],\"pem\":[\"alloc\",\"der/pem\",\"pkcs8/pem\"],\"point\":[\"dep:base16ct\",\"dep:generic-array\"],\"serde\":[\"dep:serdect\"],\"std\":[\"alloc\",\"der?/std\"],\"zeroize\":[\"dep:zeroize\",\"der?/zeroize\"]}}", "security-framework-sys_2.15.0": "{\"dependencies\":[{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"name\":\"libc\",\"req\":\"^0.2.150\"}],\"features\":{\"OSX_10_10\":[\"OSX_10_9\"],\"OSX_10_11\":[\"OSX_10_10\"],\"OSX_10_12\":[\"OSX_10_11\"],\"OSX_10_13\":[\"OSX_10_12\"],\"OSX_10_14\":[\"OSX_10_13\"],\"OSX_10_15\":[\"OSX_10_14\"],\"OSX_10_9\":[],\"default\":[\"OSX_10_12\"]}}", "security-framework_3.5.1": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.6\"},{\"name\":\"core-foundation\",\"req\":\"^0.10\"},{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"libc\",\"req\":\"^0.2.139\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"name\":\"security-framework-sys\",\"req\":\"^2.15\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.12.0\"},{\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.23\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.16\"}],\"features\":{\"OSX_10_12\":[\"security-framework-sys/OSX_10_12\"],\"OSX_10_13\":[\"OSX_10_12\",\"security-framework-sys/OSX_10_13\",\"alpn\",\"session-tickets\"],\"OSX_10_14\":[\"OSX_10_13\",\"security-framework-sys/OSX_10_14\"],\"OSX_10_15\":[\"OSX_10_14\",\"security-framework-sys/OSX_10_15\"],\"alpn\":[],\"default\":[\"OSX_10_12\"],\"job-bless\":[],\"nightly\":[],\"session-tickets\":[],\"sync-keychain\":[\"OSX_10_13\"]}}", @@ -1220,8 +1218,8 @@ "serde_urlencoded_0.7.1": "{\"dependencies\":[{\"name\":\"form_urlencoded\",\"req\":\"^1\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"name\":\"serde\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{}}", "serde_with_3.15.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"chrono_0_4\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4.20\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"expect-test\",\"req\":\"^1.5.1\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_14\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.14.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_15\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_16\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.16.0\"},{\"default_features\":false,\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4.3\"},{\"default_features\":false,\"features\":[\"serde-1\"],\"name\":\"indexmap_1\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^1.8\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"indexmap_2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"resolve-file\"],\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.33.0\"},{\"kind\":\"dev\",\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.12.1\"},{\"kind\":\"dev\",\"name\":\"rmp-serde\",\"req\":\"^1.3.0\"},{\"kind\":\"dev\",\"name\":\"ron\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"name\":\"schemars_0_8\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^0.8.16\"},{\"kind\":\"dev\",\"name\":\"schemars_0_8\",\"package\":\"schemars\",\"req\":\"^0.8.16\"},{\"default_features\":false,\"name\":\"schemars_0_9\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"schemars_0_9\",\"package\":\"schemars\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"schemars_1\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"schemars_1\",\"package\":\"schemars\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde-xml-rs\",\"req\":\"^0.8.1\"},{\"default_features\":false,\"features\":[\"result\"],\"name\":\"serde_core\",\"req\":\"^1.0.225\"},{\"default_features\":false,\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.145\"},{\"features\":[\"preserve_order\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.124\"},{\"name\":\"serde_with_macros\",\"optional\":true,\"req\":\"=3.15.1\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"name\":\"time_0_3\",\"optional\":true,\"package\":\"time\",\"req\":\"~0.3.36\"}],\"features\":{\"alloc\":[\"serde_core/alloc\",\"base64?/alloc\",\"chrono_0_4?/alloc\",\"hex?/alloc\",\"serde_json?/alloc\",\"time_0_3?/alloc\"],\"base64\":[\"dep:base64\",\"alloc\"],\"chrono\":[\"chrono_0_4\"],\"chrono_0_4\":[\"dep:chrono_0_4\"],\"default\":[\"std\",\"macros\"],\"guide\":[\"dep:document-features\",\"macros\",\"std\"],\"hashbrown_0_14\":[\"dep:hashbrown_0_14\",\"alloc\"],\"hashbrown_0_15\":[\"dep:hashbrown_0_15\",\"alloc\"],\"hashbrown_0_16\":[\"dep:hashbrown_0_16\",\"alloc\"],\"hex\":[\"dep:hex\",\"alloc\"],\"indexmap\":[\"indexmap_1\"],\"indexmap_1\":[\"dep:indexmap_1\",\"alloc\"],\"indexmap_2\":[\"dep:indexmap_2\",\"alloc\"],\"json\":[\"dep:serde_json\",\"alloc\"],\"macros\":[\"dep:serde_with_macros\"],\"schemars_0_8\":[\"dep:schemars_0_8\",\"std\",\"serde_with_macros?/schemars_0_8\"],\"schemars_0_9\":[\"dep:schemars_0_9\",\"alloc\",\"serde_with_macros?/schemars_0_9\",\"dep:serde_json\"],\"schemars_1\":[\"dep:schemars_1\",\"alloc\",\"serde_with_macros?/schemars_1\",\"dep:serde_json\"],\"std\":[\"alloc\",\"serde_core/std\",\"chrono_0_4?/clock\",\"chrono_0_4?/std\",\"indexmap_1?/std\",\"indexmap_2?/std\",\"time_0_3?/serde-well-known\",\"time_0_3?/std\",\"schemars_0_9?/std\",\"schemars_1?/std\"],\"time_0_3\":[\"dep:time_0_3\"]}}", "serde_with_macros_3.15.1": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.21.0\"},{\"kind\":\"dev\",\"name\":\"expect-test\",\"req\":\"^1.5.1\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.3\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.1\"},{\"name\":\"quote\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.12.1\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.22\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.25\"},{\"features\":[\"extra-traits\",\"full\",\"parsing\"],\"name\":\"syn\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.111\"}],\"features\":{\"schemars_0_8\":[],\"schemars_0_9\":[],\"schemars_1\":[]}}", - "serial_test_3.2.0": "{\"dependencies\":[{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"env_logger\",\"optional\":true,\"req\":\">=0.6.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"fslock\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"executor\"],\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"use_std\"],\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\">=0.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\">=0.4.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"once_cell\",\"req\":\"^1.19\"},{\"default_features\":false,\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"default_features\":false,\"name\":\"scc\",\"req\":\"^2\"},{\"name\":\"serial_test_derive\",\"req\":\"~3.2.0\"}],\"features\":{\"async\":[\"dep:futures\",\"serial_test_derive/async\"],\"default\":[\"logging\",\"async\"],\"docsrs\":[\"dep:document-features\"],\"file_locks\":[\"dep:fslock\"],\"logging\":[\"dep:log\"],\"test_logging\":[\"logging\",\"dep:env_logger\",\"serial_test_derive/test_logging\"]}}", - "serial_test_derive_3.2.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\">=0.6.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"full\",\"printing\",\"parsing\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"async\":[],\"default\":[],\"test_logging\":[]}}", + "serial_test_3.5.0": "{\"dependencies\":[{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"env_logger\",\"optional\":true,\"req\":\">=0.6.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"fslock\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-executor\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"use_std\"],\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\">=0.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\">=0.4.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"once_cell\",\"req\":\"^1.19\"},{\"default_features\":false,\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"name\":\"serial_test_derive\",\"req\":\"~3.5.0\"}],\"features\":{\"async\":[\"dep:futures-executor\",\"dep:futures-util\",\"serial_test_derive/async\"],\"default\":[\"logging\",\"async\"],\"docsrs\":[\"dep:document-features\"],\"file_locks\":[\"dep:fslock\"],\"logging\":[\"dep:log\"],\"test_logging\":[\"logging\",\"dep:env_logger\",\"serial_test_derive/test_logging\"]}}", + "serial_test_derive_3.5.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\">=0.6.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"full\",\"printing\",\"parsing\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"async\":[],\"default\":[],\"file_locks\":[],\"test_logging\":[]}}", "sha1_0.10.6": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"},{\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"sha1-asm\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"}],\"features\":{\"asm\":[\"sha1-asm\"],\"compress\":[],\"default\":[\"std\"],\"force-soft\":[],\"loongarch64_asm\":[],\"oid\":[\"digest/oid\"],\"std\":[\"digest/std\"]}}", "sha1_smol_1.0.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"openssl\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.4\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"alloc\":[],\"std\":[\"alloc\"]}}", "sha2_0.10.9": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"sha2-asm\",\"optional\":true,\"req\":\"^0.6.1\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"}],\"features\":{\"asm\":[\"sha2-asm\"],\"asm-aarch64\":[\"asm\"],\"compress\":[],\"default\":[\"std\"],\"force-soft\":[],\"force-soft-compact\":[],\"loongarch64_asm\":[],\"oid\":[\"digest/oid\"],\"std\":[\"digest/std\"]}}", From 7eaf2090942ee50158dd02fd592bc93958b1ad06 Mon Sep 17 00:00:00 2001 From: Ryaan Date: Tue, 7 Jul 2026 17:59:28 +0100 Subject: [PATCH 029/144] Fix duplicate phrase in CONTRIBUTING.md (#2522) Co-authored-by: Marcus Eagan --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 283059764..f0624105e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -201,7 +201,7 @@ NativeLink doesn't allow direct commits or human-created side branches in the ``` 5. Go to https://github.com/TraceMachina/nativelink/pulls where you should see a - button that you can click to create to create a new pull request from your + button that you can click to create a new pull request from your fork to the main repository. 6. Once you opened the pull request, click on the purple `Reviewable` button in From 7909e32c4a9bd3ea3f402c397572dd14d5dafbff Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Tue, 7 Jul 2026 23:29:23 +0530 Subject: [PATCH 030/144] web: add MCP AI widget, MDX support, and design updates (#2488) Co-authored-by: Tom Parker-Shemilt --- .../vocabularies/TraceMachina/accept.txt | 1 + .vale.ini | 4 + web/apps/web/app/community/page.tsx | 2 +- web/apps/web/app/company/page.tsx | 2 +- web/apps/web/app/contact/actions.ts | 43 ---- web/apps/web/app/contact/contact-form.tsx | 166 --------------- web/apps/web/app/contact/page.tsx | 25 +-- web/apps/web/app/globals.css | 67 ++++++ web/apps/web/app/layout.tsx | 3 +- web/apps/web/app/newsletter-action.ts | 28 --- web/apps/web/app/opengraph-image.tsx | 2 +- web/apps/web/app/page.tsx | 27 ++- web/apps/web/app/product/page.tsx | 6 +- web/apps/web/app/terms/page.tsx | 4 +- web/apps/web/components/mcp-demo.tsx | 197 ++++++++++++++++++ web/apps/web/mdx-components.tsx | 5 + web/apps/web/next.config.mjs | 11 +- web/apps/web/package.json | 4 + web/bun.lock | 11 +- web/packages/ui/src/components/faq.tsx | 2 +- .../ui/src/components/newsletter-form.tsx | 95 --------- web/packages/ui/src/components/prose.tsx | 39 ++-- .../ui/src/components/site-footer.tsx | 20 +- web/packages/ui/src/index.ts | 4 - 24 files changed, 365 insertions(+), 403 deletions(-) delete mode 100644 web/apps/web/app/contact/actions.ts delete mode 100644 web/apps/web/app/contact/contact-form.tsx delete mode 100644 web/apps/web/app/newsletter-action.ts create mode 100644 web/apps/web/components/mcp-demo.tsx create mode 100644 web/apps/web/mdx-components.tsx delete mode 100644 web/packages/ui/src/components/newsletter-form.tsx diff --git a/.github/styles/config/vocabularies/TraceMachina/accept.txt b/.github/styles/config/vocabularies/TraceMachina/accept.txt index 764b2d974..65d05bcbe 100644 --- a/.github/styles/config/vocabularies/TraceMachina/accept.txt +++ b/.github/styles/config/vocabularies/TraceMachina/accept.txt @@ -132,6 +132,7 @@ Datadog Downsampling Brex Citrix +CIQ Menlo benchmarked [Bb]orderless diff --git a/.vale.ini b/.vale.ini index 2a5ab2484..07da1def9 100644 --- a/.vale.ini +++ b/.vale.ini @@ -31,6 +31,10 @@ BlockIgnores = (?s)(.*?) # scanning it produces spurious Vale.Repetition hits on short node labels. BlockIgnores = (?s)(.*?) +# MDX pages declare metadata via `export const metadata = …` and import +# components; those are code lines, not prose. +BlockIgnores = (?m)^(import|export)\s+.+$ + # Too harsh. The `write-good.Passive` check already covers many cases. write-good.E-Prime = NO diff --git a/web/apps/web/app/community/page.tsx b/web/apps/web/app/community/page.tsx index 2c25d3c69..33942a4e8 100644 --- a/web/apps/web/app/community/page.tsx +++ b/web/apps/web/app/community/page.tsx @@ -37,7 +37,7 @@ const channels = [ { title: "Clone the repo", body: "Public source, with module-aware licensing. File issues, send PRs, or just star us. Every contribution gets a review.", - href: "https://github.com/tracemachina/nativelink", + href: "https://github.com/TraceMachina/nativelink", label: "View on GitHub", accent: "default" as const, icon: ( diff --git a/web/apps/web/app/company/page.tsx b/web/apps/web/app/company/page.tsx index 51598a596..ce512c8ca 100644 --- a/web/apps/web/app/company/page.tsx +++ b/web/apps/web/app/company/page.tsx @@ -152,7 +152,7 @@ export default function CompanyPage() { {c.title} {c.body} - +