diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..0e465b2d --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index f470ddc2..8ab16651 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -17,20 +17,21 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + submodules: true - uses: chainguard-dev/actions/setup-gitsign@main + - uses: cargo-bins/cargo-binstall@main - name: Install cargo-release - uses: taiki-e/install-action@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tool: cargo-release + run: cargo binstall cargo-release -y - name: Update versions id: update_versions run: | + OLD_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -rc '.packages[] | select(.name=="worker") | .version') cargo release version ${{ inputs.level }} --execute --no-confirm cargo update NEW_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -rc '.packages[] | select(.name=="worker") | .version') + find templates | grep Cargo.toml | while read f; do sed -i -e "s/$OLD_VERSION/$NEW_VERSION/g" $f; done echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT" - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 55a3246f..d0b8463e 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -10,11 +10,13 @@ name: Create Release jobs: github-release: name: Github Release - if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'releases/') + if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'releases/') && github.event.pull_request.head.repo.full_name == 'cloudflare/workers-rs' runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + submodules: true - name: Extract version id: extract_version run: | diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index db39b63c..afd40bbf 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -1,22 +1,142 @@ name: Pull Request on: -- pull_request -- workflow_dispatch + - pull_request + - workflow_dispatch + +permissions: + contents: read jobs: + worker-build: + name: Test & Build worker-build + runs-on: ubuntu-latest + + steps: + - uses: dtolnay/rust-toolchain@1.82.0 + - uses: actions/checkout@v4 + with: + submodules: true + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: ~/.cargo/registry ~/.cargo/git target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + - run: cargo test -p worker-build + - run: cargo build -p worker-build + - uses: actions/upload-artifact@v4 + with: + name: worker-build + path: target/debug/worker-build + + build-templates: + name: Build Templates + needs: worker-build + runs-on: ubuntu-latest + + steps: + - uses: dtolnay/rust-toolchain@1.82.0 + + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: ~/.cargo/registry ~/.cargo/git target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + - uses: actions/download-artifact@v4 + with: + name: worker-build + path: ./target/debug + + - name: Make worker-build executable + run: chmod +x ./target/debug/worker-build + + - uses: cargo-bins/cargo-binstall@main + + - name: Install cargo-generate + run: cargo binstall cargo-generate + + - name: Generate and build all templates + run: | + mkdir -p generated + for template in templates/*/; do + template_name=$(basename "$template") + if [ "$template_name" = "leptos" ]; then + echo "Skipping leptos template" + continue + fi + echo "Generating $template_name" + cargo generate --path $template --name "$template_name" --destination ./generated --force + cd "generated/$template_name" + echo "Building $template_name" + ../../target/debug/worker-build + cd ../.. + done + + build-examples: + name: Build Examples + needs: worker-build + runs-on: ubuntu-latest + steps: + - uses: dtolnay/rust-toolchain@1.82.0 + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: ~/.cargo/registry ~/.cargo/git target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + - uses: actions/download-artifact@v4 + with: + name: worker-build + path: ./target/debug + + - name: Make worker-build executable + run: chmod +x ./target/debug/worker-build + + - name: Build all examples + run: | + sed -i 's/, "examples\/axum"//g' Cargo.toml + for example in examples/*/; do + example_name=$(basename "$example") + if [ "$example_name" = "coredump" ]; then + echo "Skipping coredump example" + continue + fi + echo "Building $example_name" + cd "$example" + ../../target/debug/worker-build + cd ../.. + done + rustfmt: name: Formatter runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@1.82.0 + with: + components: rustfmt, clippy + + - uses: actions/checkout@v4 + with: + submodules: true - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 + - name: Cache Rust dependencies + uses: actions/cache@v4 with: - toolchain: stable - target: wasm32-unknown-unknown - override: true + path: ~/.cargo/registry ~/.cargo/git target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- - name: Check Formatting run: cargo fmt --all -- --check @@ -26,59 +146,73 @@ jobs: - name: Check for idiomatic code (http) run: cargo clippy --all-features --package worker-sandbox --all-targets -- -D warnings + + - name: Check for errors + run: cargo check + test: name: Test + needs: worker-build runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@1.82.0 + - uses: actions/checkout@v4 with: - toolchain: stable - target: wasm32-unknown-unknown - override: true + submodules: true - - name: Check for errors - uses: actions-rs/cargo@v1 + - uses: actions/download-artifact@v4 with: - command: check + name: worker-build + path: ./target/debug - - name: Run builder tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path worker-build/Cargo.toml - - - name: Setup node - uses: actions/setup-node@v3 + - name: Make worker-build executable + run: chmod +x ./target/debug/worker-build + + - name: Cache Rust dependencies + uses: actions/cache@v4 with: - node-version: v18.12.1 + path: ~/.cargo/registry ~/.cargo/git target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- - name: Install packages run: | sudo apt update sudo apt clean - sudo apt install -y build-essential libssl-dev netcat libc++-dev - - name: Install wasmpack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - - name: Build local worker-build - uses: actions-rs/cargo@v1 - with: - command: install - args: --path ./worker-build --force --debug + sudo apt install -y build-essential libssl-dev netcat-traditional libc++-dev - name: Install npm dependencies - shell: bash run: npm ci + - name: Build Wasm Bindgen + run: cd wasm-bindgen && cargo build -p wasm-bindgen-cli --bin wasm-bindgen --bin wasm-bindgen-test-runner + + - name: Install wasm32 target + run: rustup target add wasm32-unknown-unknown + + - name: Run wasm-bindgen tests + env: + CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER: ${{ github.workspace }}/wasm-bindgen/target/debug/wasm-bindgen-test-runner + run: cargo test -p worker --target wasm32-unknown-unknown + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Container + uses: docker/build-push-action@v6 + with: + context: ./test/container-echo + push: false + load: true + tags: worker-dev/echocontainer:latest + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Run tests - shell: bash + env: + TEST_CONTAINER_NAME: worker-dev/echocontainer:latest run: npm run test - name: Run tests (http) - shell: bash run: npm run test-http diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 00000000..b40314b3 --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,24 @@ +on: + pull_request: {} + workflow_dispatch: {} + push: + branches: + - main + - master + schedule: + - cron: '0 0 * * *' +name: Semgrep config +jobs: + semgrep: + name: semgrep/ci + runs-on: ubuntu-latest + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + SEMGREP_URL: https://cloudflare.semgrep.dev + SEMGREP_APP_URL: https://cloudflare.semgrep.dev + SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version + container: + image: returntocorp/semgrep + steps: + - uses: actions/checkout@v4 + - run: semgrep ci diff --git a/.gitignore b/.gitignore index dcb11df6..24d4c185 100644 --- a/.gitignore +++ b/.gitignore @@ -1,139 +1,7 @@ -**/target +target .DS_Store -**/worker/generated/* -worker-sandbox/build - -#### Node -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# RustRover -.idea +build +generated +package-lock.json +.wrangler +node_modules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1f6decbe --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wasm-bindgen"] + path = wasm-bindgen + url = https://github.com/wasm-bindgen/wasm-bindgen diff --git a/Cargo.lock b/Cargo.lock index 02cf66c8..42b6b5b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,19 +3,10 @@ version = 3 [[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli 0.29.0", -] - -[[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -28,6 +19,18 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -37,12 +40,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -54,9 +51,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -69,161 +66,143 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] -name = "atty" -version = "0.2.14" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.7.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ - "async-trait", "axum-core", + "base64", "bytes", + "form_urlencoded", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", + "hyper", + "hyper-util", "itoa", - "matchit", + "matchit 0.8.4", "memchr", "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper 1.0.1", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-core" -version = "0.4.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ - "async-trait", "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "futures-core", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.72", -] - -[[package]] -name = "backtrace" -version = "0.3.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "syn", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -242,9 +221,9 @@ dependencies = [ "fs4", "hex", "is_executable", - "siphasher", + "siphasher 0.3.11", "tar", - "ureq", + "ureq 2.12.1", "zip", ] @@ -256,9 +235,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "blake2" @@ -280,9 +259,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -292,113 +271,117 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ "bzip2-sys", - "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] [[package]] name = "camino" -version = "1.1.7" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-util-schemas" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e63d2780ac94487eb9f1fea7b0d56300abc9eb488800854ca217f102f5caccca" dependencies = [ + "semver", "serde", + "serde-untagged", + "serde-value", + "thiserror 1.0.69", + "toml 0.8.23", + "unicode-xid", + "url", ] [[package]] name = "cargo_metadata" -version = "0.15.4" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502" dependencies = [ "camino", "cargo-platform", + "cargo-util-schemas", "semver", "serde", "serde_json", - "thiserror", + "thiserror 2.0.17", ] [[package]] name = "cc" -version = "1.1.6" +version = "1.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" dependencies = [ + "find-msvc-tools", "jobserver", "libc", + "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] name = "chrono-tz" -version = "0.8.6" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" dependencies = [ "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", + "phf 0.12.1", ] [[package]] @@ -413,9 +396,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.11" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" dependencies = [ "clap_builder", "clap_derive", @@ -423,68 +406,66 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "console" -version = "0.15.8" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" dependencies = [ "encode_unicode", - "lazy_static", "libc", + "once_cell", "unicode-width", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "constant_time_eq" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +name = "container-echo" +version = "0.1.5" +dependencies = [ + "axum", + "tokio", +] [[package]] name = "convert_case" @@ -495,6 +476,35 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap 2.12.0", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -507,24 +517,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -537,18 +547,37 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -593,57 +622,45 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "deflate64" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" +checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", -] - -[[package]] -name = "dialoguer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" -dependencies = [ - "console", - "shell-words", - "tempfile", - "zeroize", + "syn", ] [[package]] @@ -694,53 +711,70 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", ] [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] -name = "env_logger" -version = "0.10.2" +name = "env_home" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "log", -] +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +dependencies = [ + "serde", + "serde_core", + "typeid", +] [[package]] name = "errno" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -751,27 +785,42 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fetcher-on-workers" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "worker", +] [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "flate2" -version = "1.0.30" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -783,6 +832,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -800,9 +855,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -813,7 +868,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" dependencies = [ - "rustix", + "rustix 0.38.44", "windows-sys 0.48.0", ] @@ -825,9 +880,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -835,44 +890,44 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", @@ -887,9 +942,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -897,14 +952,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", "wasm-bindgen", ] @@ -920,30 +987,36 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.29.0" +name = "glob" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] -name = "glob" -version = "0.3.1" +name = "gloo-timers" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] [[package]] name = "h2" -version = "0.3.26" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.2.6", + "http", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", @@ -961,21 +1034,26 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "serde", +] [[package]] -name = "heck" -version = "0.3.3" +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "unicode-segmentation", + "foldhash", + "serde", ] [[package]] -name = "heck" -version = "0.4.1" +name = "hashbrown" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -985,18 +1063,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1013,48 +1082,17 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1062,27 +1100,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "futures-core", + "http", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.4" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1090,69 +1128,98 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "human-panic" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f016c89920bbb30951a8405ecacbb4540db5524313b9445736e7e1855cf370" -dependencies = [ - "anstream", - "anstyle", - "backtrace", - "os_info", - "serde", - "serde_derive", - "toml 0.8.16", - "uuid", -] - [[package]] name = "hyper" -version = "0.14.30" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", "futures-core", - "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.6", + "http", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "pin-utils", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1166,20 +1233,120 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +dependencies = [ + "rayon", +] [[package]] name = "idna" -version = "0.5.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1194,29 +1361,40 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] [[package]] name = "is_executable" @@ -1229,31 +1407,31 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +version = "0.3.82" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1261,10 +1439,8 @@ dependencies = [ name = "kv-on-workers" version = "0.1.0" dependencies = [ - "js-sys", "serde_json", - "wasm-bindgen", - "wasm-bindgen-futures", + "worker", "worker-kv", ] @@ -1280,49 +1456,67 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.155" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.10.0", "libc", + "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "litrs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - [[package]] name = "log" -version = "0.4.22" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lzma-rs" @@ -1334,6 +1528,17 @@ dependencies = [ "crc", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "mach" version = "0.3.2" @@ -1349,6 +1554,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -1367,9 +1578,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memoffset" @@ -1386,32 +1597,42 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -1439,12 +1660,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -1464,36 +1684,33 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] [[package]] -name = "object" -version = "0.36.2" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" -dependencies = [ - "memchr", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "once_cell" -version = "1.19.0" +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -1510,20 +1727,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" dependencies = [ "cc", "libc", @@ -1532,27 +1749,19 @@ dependencies = [ ] [[package]] -name = "os_info" -version = "3.8.2" +name = "ordered-float" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", + "num-traits", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1560,25 +1769,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] -name = "parse-zoneinfo" -version = "0.3.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path-clean" @@ -1598,73 +1804,72 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" dependencies = [ - "phf_shared", + "phf_shared 0.12.1", ] [[package]] -name = "phf_codegen" -version = "0.11.2" +name = "phf" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_generator", - "phf_shared", + "phf_shared 0.13.1", + "serde", ] [[package]] -name = "phf_generator" -version = "0.11.2" +name = "phf_shared" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" dependencies = [ - "phf_shared", - "rand", + "siphasher 1.0.1", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1674,9 +1879,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "platforms" @@ -1686,15 +1891,15 @@ checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" [[package]] name = "postgres-protocol" -version = "0.6.7" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +checksum = "fbef655056b916eb868048276cfd5d6a7dea4f81560dfd047f97c8c6fe3fcfd4" dependencies = [ - "base64 0.22.1", + "base64", "byteorder", "bytes", "fallible-iterator", - "getrandom", + "getrandom 0.3.4", "hmac", "md-5", "memchr", @@ -1705,15 +1910,24 @@ dependencies = [ [[package]] name = "postgres-types" -version = "0.2.7" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02048d9e032fb3cc3413bbf7b83a15d84a5d419778e2628751896d856498eee9" +checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095" dependencies = [ "bytes", "fallible-iterator", "postgres-protocol", ] +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1722,25 +1936,28 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.72", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -1759,7 +1976,7 @@ dependencies = [ "num_cpus", "once_cell", "platforms", - "thiserror", + "thiserror 1.0.69", "unescape", ] @@ -1769,35 +1986,39 @@ version = "0.1.0" dependencies = [ "js-sys", "serde", - "wasm-bindgen", "worker", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" -version = "0.8.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -1805,47 +2026,58 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom", + "getrandom 0.3.4", ] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "rayon" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ - "bitflags 1.3.2", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", ] [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.10.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.16", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -1855,9 +2087,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -1866,70 +2098,69 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "encoding_rs", "futures-core", - "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.6", + "http", + "http-body", + "http-body-util", "hyper", + "hyper-rustls", "hyper-tls", - "ipnet", + "hyper-util", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", + "sync_wrapper", "tokio", "tokio-native-tls", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", ] [[package]] name = "retry" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9166d72162de3575f950507683fac47e30f6f2c3836b71b7fbc61aa517c9c5f4" +checksum = "a1e211f878258887b3e65dd3c8ff9f530fe109f441a117ee0cdc27f341355032" dependencies = [ "rand", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -1948,8 +2179,7 @@ name = "rust-rpc-client" version = "0.1.0" dependencies = [ "async-trait", - "console_error_panic_hook", - "serde-wasm-bindgen 0.6.5", + "serde-wasm-bindgen", "wasm-bindgen", "worker", "worker-codegen", @@ -1959,35 +2189,46 @@ dependencies = [ name = "rust-rpc-server" version = "0.1.0" dependencies = [ - "console_error_panic_hook", - "wasm-bindgen", "worker", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" dependencies = [ "log", "once_cell", @@ -2000,24 +2241,27 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.21.7", + "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "ring", "rustls-pki-types", @@ -2026,15 +2270,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -2047,19 +2291,13 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2072,7 +2310,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.10.0", "core-foundation", "core-foundation-sys", "libc", @@ -2081,9 +2319,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -2091,31 +2329,44 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.204" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" +name = "serde-untagged" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" dependencies = [ - "js-sys", + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", "serde", - "wasm-bindgen", ] [[package]] @@ -2129,46 +2380,78 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "serde_ignored" -version = "0.1.10" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf" +checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", + "memchr", "ryu", "serde", + "serde_core", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2194,9 +2477,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2213,16 +2496,16 @@ dependencies = [ ] [[package]] -name = "shell-words" -version = "1.1.0" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -2239,42 +2522,39 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.7" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stringprep" @@ -2287,12 +2567,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -2307,9 +2581,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -2317,44 +2591,41 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.72" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "futures-core", ] [[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.10.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -2362,9 +2633,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.41" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -2373,51 +2644,71 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "cfg-if", "fastrand", - "rustix", - "windows-sys 0.52.0", + "getrandom 0.3.4", + "once_cell", + "rustix 1.1.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "1.0.63" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -2431,25 +2722,35 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -2462,11 +2763,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", @@ -2475,18 +2775,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] @@ -2501,9 +2801,9 @@ dependencies = [ [[package]] name = "tokio-postgres" -version = "0.7.11" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03adcf0147e203b6032c0b2d30be1415ba03bc348901f3ff1cc0df6a733e60c3" +checksum = "2b40d66d9b2cfe04b628173409368e58247e8eddbbd3b0e6c6ba1d09f20f6c9e" dependencies = [ "async-trait", "byteorder", @@ -2514,7 +2814,7 @@ dependencies = [ "log", "parking_lot", "percent-encoding", - "phf", + "phf 0.13.1", "pin-project-lite", "postgres-protocol", "postgres-types", @@ -2534,22 +2834,44 @@ dependencies = [ "worker", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -2560,94 +2882,137 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.8" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit", ] [[package]] name = "toml" -version = "0.8.16" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.17", + "indexmap 2.12.0", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.7" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.12.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", "winnow", ] [[package]] -name = "toml_edit" -version = "0.22.17" +name = "toml_parser" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", + "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2655,20 +3020,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -2698,9 +3063,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -2708,9 +3073,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "nu-ansi-term", "serde", @@ -2745,28 +3110,49 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" dependencies = [ - "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "rand", "sha1", - "thiserror", - "url", + "thiserror 2.0.17", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror 2.0.17", "utf-8", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unescape" @@ -2776,48 +3162,48 @@ checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" @@ -2827,31 +3213,63 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64 0.22.1", + "base64", "flate2", "log", "once_cell", "rustls", "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "ureq" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ba1025f18a4a3fc3e9b48c868e9beb4f24f4b4b1a325bada26bd4119f46537" +dependencies = [ + "base64", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", - "url", - "webpki-roots", + "ureq-proto", + "utf-8", + "webpki-roots 1.0.3", +] + +[[package]] +name = "ureq-proto" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" +dependencies = [ + "base64", + "http", + "httparse", + "log", ] [[package]] name = "url" -version = "2.5.2" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2860,6 +3278,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2868,19 +3292,21 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom", + "getrandom 0.3.4", + "js-sys", "serde", + "wasm-bindgen", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -2890,9 +3316,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -2906,30 +3332,31 @@ dependencies = [ [[package]] name = "walrus" -version = "0.20.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7" +checksum = "3b5fbda74aece555fd16909d66141a934c9db314980a98800cf138a00c3e23a8" dependencies = [ "anyhow", - "gimli 0.26.2", + "gimli", "id-arena", "leb128", "log", + "rayon", "walrus-macro", "wasm-encoder", - "wasmparser 0.80.2", + "wasmparser 0.240.0", ] [[package]] name = "walrus-macro" -version = "0.19.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" +checksum = "0ef06db404cbaed87cb25fd2ca3a62502af485f43383c9641ffcf1479d02fffd" dependencies = [ - "heck 0.3.3", + "heck", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -2943,9 +3370,18 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] [[package]] name = "wasite" @@ -2955,78 +3391,45 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +version = "0.2.105" dependencies = [ "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", "once_cell", - "proc-macro2", - "quote", - "syn 2.0.72", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-cli-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca821da8c1ae6c87c5e94493939a206daa8587caff227c6032e0061a3d80817f" +version = "0.2.105" dependencies = [ "anyhow", - "base64 0.21.7", + "base64", + "leb128", "log", "rustc-demangle", + "serde", "serde_json", - "tempfile", - "unicode-ident", "walrus", - "wasm-bindgen-externref-xform", - "wasm-bindgen-multi-value-xform", "wasm-bindgen-shared", - "wasm-bindgen-threads-xform", - "wasm-bindgen-wasm-conventions", - "wasm-bindgen-wasm-interpreter", -] - -[[package]] -name = "wasm-bindgen-externref-xform" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102582726b35a30d53157fbf8de3d0f0fed4c40c0c7951d69a034e9ef01da725" -dependencies = [ - "anyhow", - "walrus", + "wasmparser 0.214.0", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +version = "0.4.55" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +version = "0.2.105" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3034,42 +3437,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +version = "0.2.105" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.72", - "wasm-bindgen-backend", + "syn", "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-multi-value-xform" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3498e4799f43523d780ceff498f04d882a8dbc9719c28020034822e5952f32a4" +name = "wasm-bindgen-shared" +version = "0.2.105" dependencies = [ - "anyhow", - "walrus", + "unicode-ident", ] -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - [[package]] name = "wasm-bindgen-test" -version = "0.3.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +version = "0.3.55" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -3077,158 +3466,118 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +version = "0.3.55" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] [[package]] -name = "wasm-bindgen-threads-xform" -version = "0.2.92" +name = "wasm-encoder" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5add359b7f7d09a55299a9d29be54414264f2b8cf84f8c8fda5be9269b5dd9" +checksum = "06d642d8c5ecc083aafe9ceb32809276a304547a3a6eeecceb5d8152598bc71f" dependencies = [ - "anyhow", - "walrus", - "wasm-bindgen-wasm-conventions", + "leb128fmt", + "wasmparser 0.240.0", ] [[package]] -name = "wasm-bindgen-wasm-conventions" -version = "0.2.92" +name = "wasm-streams" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c04e3607b810e76768260db3a5f2e8beb477cb089ef8726da85c8eb9bd3b575" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ - "anyhow", - "walrus", + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "wasm-bindgen-wasm-interpreter" -version = "0.2.92" +name = "wasmparser" +version = "0.205.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea966593c8243a33eb4d643254eb97a69de04e89462f46cf6b4f506aae89b3a" +checksum = "1d457bb52804242e09d55a306e53ddbc65d1d29ed83db6a4eea3ed412ee0cfdf" dependencies = [ - "anyhow", - "log", - "walrus", - "wasm-bindgen-wasm-conventions", + "bitflags 2.10.0", + "indexmap 2.12.0", + "semver", ] [[package]] -name = "wasm-encoder" -version = "0.29.0" +name = "wasmparser" +version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" +checksum = "5309c1090e3e84dad0d382f42064e9933fdaedb87e468cc239f0eabea73ddcb6" dependencies = [ - "leb128", + "ahash", + "bitflags 2.10.0", + "hashbrown 0.14.5", + "indexmap 2.12.0", + "semver", + "serde", ] [[package]] -name = "wasm-pack" -version = "0.13.0" +name = "wasmparser" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcb9f254996b4bbce2c7d738273739ce515834a49b1910e85a6a3fee5b6405d" +checksum = "b722dcf61e0ea47440b53ff83ccb5df8efec57a69d150e4f24882e4eba7e24a4" dependencies = [ - "anyhow", - "atty", - "binary-install", - "cargo_metadata", - "chrono", - "clap", - "console", - "dialoguer", - "env_logger", - "glob", - "human-panic", - "log", - "parking_lot", - "path-clean", + "bitflags 2.10.0", + "hashbrown 0.15.5", + "indexmap 2.12.0", "semver", "serde", - "serde_derive", - "serde_ignored", - "serde_json", - "siphasher", - "strsim 0.10.0", - "toml 0.7.8", - "ureq", - "walkdir", - "which", ] [[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +name = "web-sys" +version = "0.3.82" dependencies = [ - "futures-util", "js-sys", "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", ] [[package]] -name = "wasmparser" -version = "0.80.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" - -[[package]] -name = "wasmparser" -version = "0.205.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d457bb52804242e09d55a306e53ddbc65d1d29ed83db6a4eea3ed412ee0cfdf" -dependencies = [ - "bitflags 2.6.0", - "indexmap 2.2.6", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.69" +name = "webpki-roots" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "js-sys", - "wasm-bindgen", + "webpki-roots 1.0.3", ] [[package]] name = "webpki-roots" -version = "0.26.3" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" dependencies = [ "rustls-pki-types", ] [[package]] name = "which" -version = "4.4.2" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ - "either", - "home", - "once_cell", - "rustix", + "env_home", + "rustix 1.1.2", + "winsafe", ] [[package]] name = "whoami" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall 0.4.1", + "libredox", "wasite", "web-sys", ] @@ -3251,11 +3600,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3266,11 +3615,96 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -3291,6 +3725,33 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3315,13 +3776,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3334,6 +3812,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3346,6 +3830,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3358,12 +3848,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3376,6 +3878,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3388,6 +3896,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3400,6 +3914,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3412,24 +3932,32 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" -version = "0.5.40" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.50.0" +name = "winsafe" +version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "wit-parser" @@ -3439,7 +3967,7 @@ checksum = "a3db34c7688c161ed7bd1b2f8055dca9fb2c15201db58754e9c48a0805f32e5f" dependencies = [ "anyhow", "id-arena", - "indexmap 2.2.6", + "indexmap 2.12.0", "log", "semver", "serde", @@ -3451,7 +3979,7 @@ dependencies = [ [[package]] name = "worker" -version = "0.3.1" +version = "0.6.7" dependencies = [ "async-trait", "axum", @@ -3460,13 +3988,13 @@ dependencies = [ "chrono-tz", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "js-sys", - "matchit", + "matchit 0.7.3", "pin-project", "serde", - "serde-wasm-bindgen 0.6.5", + "serde-wasm-bindgen", "serde_json", "serde_urlencoded", "tokio", @@ -3474,38 +4002,51 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-bindgen-test", "wasm-streams", "web-sys", - "worker-kv", "worker-macros", "worker-sys", ] [[package]] name = "worker-build" -version = "0.1.0" +version = "0.1.13" dependencies = [ "anyhow", + "binary-install", + "cargo_metadata", "clap", + "console", "dirs-next", "flate2", + "glob", + "log", + "parking_lot", + "path-clean", + "semver", + "serde", + "serde_ignored", + "serde_json", + "strsim", "tar", - "ureq", + "toml 0.9.8", + "ureq 3.1.2", "wasm-bindgen-cli-support", - "wasm-pack", + "which", "worker-codegen", ] [[package]] name = "worker-codegen" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "convert_case", "prettyplease", "proc-macro2", "quote", - "syn 2.0.72", + "syn", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -3514,29 +4055,30 @@ dependencies = [ [[package]] name = "worker-kv" -version = "0.7.0" +version = "0.9.1" dependencies = [ "fs_extra", "js-sys", "psutil", "reqwest", "serde", - "serde-wasm-bindgen 0.5.0", + "serde-wasm-bindgen", "serde_json", - "thiserror", + "thiserror 2.0.17", "tokio", "wasm-bindgen", "wasm-bindgen-futures", + "worker", ] [[package]] name = "worker-macros" -version = "0.3.1" +version = "0.6.7" dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.72", + "syn", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -3552,32 +4094,33 @@ dependencies = [ "blake2", "cfg-if", "chrono", - "console_error_panic_hook", "futures-channel", "futures-util", - "getrandom", + "getrandom 0.3.4", + "gloo-timers", "hex", - "http 1.1.0", + "http", "md5", + "paste", "rand", "regex", "retry", "serde", - "serde-wasm-bindgen 0.6.5", + "serde-wasm-bindgen", "serde_json", "tokio", "tokio-stream", "tower-service", - "tungstenite", + "tungstenite 0.27.0", "uuid", + "wasm-bindgen", "wasm-bindgen-test", "worker", - "worker-kv", ] [[package]] name = "worker-sys" -version = "0.3.1" +version = "0.6.7" dependencies = [ "cfg-if", "js-sys", @@ -3585,22 +4128,101 @@ dependencies = [ "web-sys", ] +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "xattr" -version = "1.3.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "linux-raw-sys", - "rustix", + "rustix 1.1.2", +] + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -3613,14 +4235,47 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "zip" -version = "2.1.5" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b895748a3ebcb69b9d38dcfdf21760859a4b0d0b0015277640c2ef4c69640e6f" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ "aes", "arbitrary", @@ -3631,15 +4286,16 @@ dependencies = [ "deflate64", "displaydoc", "flate2", + "getrandom 0.3.4", "hmac", - "indexmap 2.2.6", + "indexmap 2.12.0", "lzma-rs", "memchr", "pbkdf2", - "rand", "sha1", - "thiserror", + "thiserror 2.0.17", "time", + "xz2", "zeroize", "zopfli", "zstd", @@ -3647,41 +4303,39 @@ dependencies = [ [[package]] name = "zopfli" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", - "lockfree-object-pool", "log", - "once_cell", "simd-adler32", ] [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.0" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index b695f7fb..e4ea7186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,33 +3,47 @@ members = [ "worker", "worker-build", "worker-macros", - "worker-sandbox", + "test", "worker-sys", "worker-kv", "examples/*", + "test/container-echo" ] -exclude = ["examples/coredump", "examples/axum", "templates/*"] +exclude = ["examples/coredump", "examples/axum", "templates/*", "wasm-bindgen", "generated"] resolver = "2" [workspace.dependencies] -worker = { path = "./worker" } -async-trait = "0.1.74" -# TODO: Unpin after https://github.com/chronotope/chrono-tz/issues/152 -chrono = { version = "0.4.37", default-features = false, features = [ +async-trait = "0.1.88" +chrono = { version = "0.4.41", default-features = false, features = [ "wasmbind", ] } -futures-channel = "0.3.29" -futures-util = { version = "0.3.29", default-features = false } -http = "1.1.0" -js-sys = "0.3.66" -wasm-bindgen = "0.2.91" -wasm-bindgen-cli-support = "0.2.91" -wasm-bindgen-futures = "0.4.39" -wasm-bindgen-macro-support = "0.2.91" -wasm-bindgen-test = "0.3.39" +futures-channel = "0.3.31" +futures-util = { version = "0.3.31", default-features = false } +http = "1.3" +js-sys = { version = "0.3.82", path = "./wasm-bindgen/crates/js-sys" } +serde = { version = "1.0.164", features = ["derive"] } +serde_json = "1.0.140" +serde-wasm-bindgen = "0.6.5" +wasm-bindgen = { version = "0.2.105", path = "./wasm-bindgen" } +wasm-bindgen-cli-support = { version = "0.2.105", path = "./wasm-bindgen/crates/cli-support" } +wasm-bindgen-futures = { version = "0.4.54", path = "./wasm-bindgen/crates/futures" } +wasm-bindgen-macro-support = { version = "0.2.105", path = "./wasm-bindgen/crates/macro-support" } +wasm-bindgen-shared = { version = "0.2.105", path = "./wasm-bindgen/crates/shared" } +wasm-bindgen-test = { version = "0.3.50", path = "./wasm-bindgen/crates/test" } +web-sys = { version = "0.3.82", features = [ + "File", + "ReadableStreamDefaultReader", + "WorkerGlobalScope", + "WritableStreamDefaultWriter", +] } +worker = { path = "worker", version = "0.6.7", features = ["queue", "d1", "axum", "timezone"] } +worker-codegen = { path = "worker-codegen", version = "0.2.0" } +worker-kv = { path = "worker-kv", version = "0.9.1" } +worker-macros = { path = "worker-macros", version = "0.6.7", features = ["queue"] } +worker-sys = { path = "worker-sys", version = "0.6.7", features = ["d1", "queue"] } [profile.release] -# rustc supports two "optimize for size" levels: opt-level = "s" and "z". +# rustc supports two "optimize for size" levels: opt-level = "s" and "z". # These names were inherited from clang / LLVM and are not too descriptive # but "z" is meant to give the idea that it produces smaller binaries than "s". # https://docs.rust-embedded.org/book/unsorted/speed-vs-size.html#optimize-for-size @@ -50,3 +64,13 @@ lto = true [profile.release.package."*"] codegen-units = 1 opt-level = "z" + +[patch.crates-io] +js-sys = { version = "0.3.82", path = './wasm-bindgen/crates/js-sys' } +wasm-bindgen = { version = "0.2.105", path = './wasm-bindgen' } +wasm-bindgen-cli-support = { version = "0.2.105", path = "./wasm-bindgen/crates/cli-support" } +wasm-bindgen-futures = { version = "0.4.55", path = './wasm-bindgen/crates/futures' } +wasm-bindgen-macro-support = { version = "0.2.105", path = "./wasm-bindgen/crates/macro-support" } +wasm-bindgen-shared = { version = "0.2.105", path = "./wasm-bindgen/crates/shared" } +wasm-bindgen-test = { version = "0.3.55", path = "./wasm-bindgen/crates/test" } +web-sys = { version = "0.3.82", path = './wasm-bindgen/crates/web-sys' } diff --git a/README.md b/README.md index f3714317..c149ed28 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Read the [Notes and FAQ](#notes-and-faq) use worker::*; #[event(fetch)] -pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result { +pub async fn main(mut req: Request, env: Env, _ctx: worker::Context) -> Result { console_log!( "{} {}, located at: {:?}, within: {}", req.method().to_string(), @@ -45,7 +45,7 @@ The project uses [wrangler](https://github.com/cloudflare/workers-sdk/tree/main/ Use [cargo generate](https://github.com/cargo-generate/cargo-generate) to start from a template: ```bash -$ cargo generate cloudflare/workers-rs +cargo generate cloudflare/workers-rs ``` There are several templates to chose from. You should see a new project layout with a `src/lib.rs`. @@ -221,11 +221,10 @@ For more information about how to configure these bindings, see: ### Define a Durable Object in Rust To define a Durable Object using the `worker` crate you need to implement the `DurableObject` trait -on your own struct. Additionally, the `#[durable_object]` attribute macro must be applied to _both_ -your struct definition and the trait `impl` block for it. +on your own struct. Additionally, the `#[durable_object]` attribute macro must be applied to the struct definition. ```rust -use worker::*; +use worker::{durable_object, State, Env, Result, Request, Response}; #[durable_object] pub struct Chatroom { @@ -235,7 +234,6 @@ pub struct Chatroom { env: Env, // access `Env` across requests, use inside `fetch` } -#[durable_object] impl DurableObject for Chatroom { fn new(state: State, env: Env) -> Self { Self { @@ -246,7 +244,7 @@ impl DurableObject for Chatroom { } } - async fn fetch(&mut self, _req: Request) -> Result { + async fn fetch(&self, _req: Request) -> Result { // do some work when a worker makes a request to this DO Response::ok(&format!("{} active users.", self.users.len())) } @@ -271,6 +269,66 @@ tag = "v1" # Should be unique for each entry new_classes = ["Chatroom"] # Array of new classes ``` +### SQLite Storage in Durable Objects + +Durable Objects can use SQLite for persistent storage, providing a relational database interface. To enable SQLite storage, you need to use `new_sqlite_classes` in your migration and access the SQL storage through `state.storage().sql()`. + +```rust +use worker::{durable_object, State, Env, Result, Request, Response, SqlStorage}; + +#[durable_object] +pub struct SqlCounter { + sql: SqlStorage, +} + +impl DurableObject for SqlCounter { + fn new(state: State, _env: Env) -> Self { + let sql = state.storage().sql(); + // Create table if it does not exist + sql.exec("CREATE TABLE IF NOT EXISTS counter(value INTEGER);", None) + .expect("create table"); + Self { sql } + } + + async fn fetch(&self, _req: Request) -> Result { + #[derive(serde::Deserialize)] + struct Row { + value: i32, + } + + // Read current value + let rows: Vec = self + .sql + .exec("SELECT value FROM counter LIMIT 1;", None)? + .to_array()?; + let current = rows.get(0).map(|r| r.value).unwrap_or(0); + let next = current + 1; + + // Update counter + self.sql.exec("DELETE FROM counter;", None)?; + self.sql + .exec("INSERT INTO counter(value) VALUES (?);", vec![next.into()])?; + + Response::ok(format!("SQL counter is now {}", next)) + } +} +``` + +Configure your `wrangler.toml` to enable SQLite storage: + +```toml +# ... + +[durable_objects] +bindings = [ + { name = "SQL_COUNTER", class_name = "SqlCounter" } +] + +[[migrations]] +tag = "v1" # Should be unique for each entry +new_sqlite_classes = ["SqlCounter"] # Use new_sqlite_classes for SQLite-enabled objects +``` + - For more information about migrating your Durable Object as it changes, see the docs here: https://developers.cloudflare.com/workers/learning/using-durable-objects#durable-object-migrations-in-wranglertoml @@ -389,7 +447,7 @@ In order to test your Rust worker locally, the best approach is to use [Miniflare](https://github.com/cloudflare/miniflare). However, because Miniflare is a Node package, you will need to write your end-to-end tests in JavaScript or TypeScript in your project. The official documentation for writing tests using -Miniflare is [available here](https://miniflare.dev/testing). This documentation +Miniflare is [available here](https://miniflare.dev). This documentation being focused on JavaScript / TypeScript codebase, you will need to configure as follows to make it work with your Rust-based, WASM-generated worker: @@ -505,7 +563,7 @@ please [take a look](https://www.cloudflare.com/careers/). 1. Can I deploy a Worker that uses `tokio` or `async_std` runtimes? - Currently no. All crates in your Worker project must compile to `wasm32-unknown-unknown` target, - which is more limited in some ways than targets for x86 and ARM64. + which is more limited in some ways than targets for x86 and ARM64. However, you should still be able to use runtime-agnostic primitives from those crates such as those from [tokio::sync](https://docs.rs/tokio/latest/tokio/sync/index.html#runtime-compatibility). 2. The `worker` crate doesn't have _X_! Why not? diff --git a/examples/axum/Cargo.lock b/examples/axum/Cargo.lock index 22c482ec..074a22c6 100644 --- a/examples/axum/Cargo.lock +++ b/examples/axum/Cargo.lock @@ -1,12 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -15,17 +30,16 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.4" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ - "async-trait", "axum-core", "bytes", "futures-util", @@ -33,7 +47,7 @@ dependencies = [ "http-body", "http-body-util", "itoa", - "matchit", + "matchit 0.8.4", "memchr", "mime", "percent-encoding", @@ -48,13 +62,12 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ - "async-trait", "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", @@ -71,36 +84,50 @@ name = "axum-on-workers" version = "0.1.0" dependencies = [ "axum", - "console_error_panic_hook", "tower-service", "wasm-bindgen-futures", "worker", "worker-macros", ] +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "bytes" -version = "1.5.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "js-sys", "num-traits", @@ -108,13 +135,14 @@ dependencies = [ ] [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "cfg-if", - "wasm-bindgen", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -134,30 +162,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -166,21 +194,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", @@ -193,11 +221,17 @@ dependencies = [ "slab", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -206,9 +240,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -216,9 +250,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", @@ -227,39 +261,146 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "libc" +version = "0.2.173" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "log" -version = "0.4.17" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchit" @@ -267,11 +408,17 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" @@ -279,20 +426,38 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "percent-encoding" @@ -302,18 +467,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -322,9 +487,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -332,56 +497,60 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde-wasm-bindgen" version = "0.6.5" @@ -395,9 +564,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -406,11 +575,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -429,18 +599,30 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" -version = "2.0.52" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -449,24 +631,35 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "thiserror" -version = "1.0.40" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -474,108 +667,95 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.28.1" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ - "autocfg", + "backtrace", "pin-project-lite", - "windows-sys", ] [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -584,21 +764,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -606,9 +787,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" dependencies = [ "proc-macro2", "quote", @@ -619,15 +800,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -638,32 +822,24 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -672,49 +848,57 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "worker" -version = "0.0.24" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bac3f217ea34f8037fa7e505203ebc8de8b8efe2a0b4fab03a1c9bbc58660f9" dependencies = [ "async-trait", "axum", @@ -725,10 +909,10 @@ dependencies = [ "http", "http-body", "js-sys", - "matchit", + "matchit 0.7.3", "pin-project", "serde", - "serde-wasm-bindgen 0.6.5", + "serde-wasm-bindgen", "serde_json", "serde_urlencoded", "tokio", @@ -744,13 +928,13 @@ dependencies = [ [[package]] name = "worker-kv" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4b9fe1a87b7aef252fceb4f30bf6303036a5de329c81ccad9be9c35d1fdbc7" +checksum = "b0d30eb90e8db0657414129624c0d12c6cb480574bc2ddd584822db196cb9a52" dependencies = [ "js-sys", "serde", - "serde-wasm-bindgen 0.5.0", + "serde-wasm-bindgen", "serde_json", "thiserror", "wasm-bindgen", @@ -759,7 +943,9 @@ dependencies = [ [[package]] name = "worker-macros" -version = "0.0.16" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23556a173fb650061d885e27eed136d84e522d1d053224311a5984c7f4434c13" dependencies = [ "async-trait", "proc-macro2", @@ -773,10 +959,96 @@ dependencies = [ [[package]] name = "worker-sys" -version = "0.0.16" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d4052ea1a737d212a10d5cc201df4e1e059582d464b15703f5797496ee6db8" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/examples/axum/Cargo.toml b/examples/axum/Cargo.toml index dfa97683..46e50bf4 100644 --- a/examples/axum/Cargo.toml +++ b/examples/axum/Cargo.toml @@ -6,17 +6,12 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { path="../../worker", features=['http', 'axum'] } -worker-macros = { path="../../worker-macros", features=['http'] } -axum = { version = "0.7", default-features = false } -tower-service = "0.3.2" -console_error_panic_hook = { version = "0.1.1" } -wasm-bindgen-futures = "0.4" \ No newline at end of file +worker = { workspace = true, features = ['http', 'axum'] } +worker-macros = { workspace = true, features = ['http'] } +axum = { version = "0.8", default-features = false } +tower-service = "0.3.3" +wasm-bindgen-futures = "0.4" diff --git a/examples/axum/package.json b/examples/axum/package.json index af220808..fcaaeba0 100644 --- a/examples/axum/package.json +++ b/examples/axum/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^2.13.0" + "wrangler": "^4" } } diff --git a/examples/axum/src/lib.rs b/examples/axum/src/lib.rs index 0f3d326d..f80ee2b9 100644 --- a/examples/axum/src/lib.rs +++ b/examples/axum/src/lib.rs @@ -12,8 +12,6 @@ async fn fetch( _env: Env, _ctx: Context, ) -> Result> { - console_error_panic_hook::set_once(); - Ok(router().call(req).await?) } diff --git a/examples/axum/wrangler.toml b/examples/axum/wrangler.toml index f434f523..8e7d841e 100644 --- a/examples/axum/wrangler.toml +++ b/examples/axum/wrangler.toml @@ -1,6 +1,5 @@ name = "axum-on-workers" -main = "build/worker/shim.mjs" -compatibility_date = "2023-03-22" +main = "build/index.js" [build] -command = "cargo install --path ../../worker-build && worker-build --release" +command = "cargo install worker-build && worker-build --release" diff --git a/examples/custom-req/Cargo.toml b/examples/custom-req/Cargo.toml index b7d077c5..3c9837e2 100644 --- a/examples/custom-req/Cargo.toml +++ b/examples/custom-req/Cargo.toml @@ -6,16 +6,12 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { workspace=true } +worker = { workspace = true } tracing = "0.1" tracing-web = "0.1" -tracing-subscriber = { version = "0.3", features=['time', 'json'] } -time = { version = "0.3", features=['wasm-bindgen'] } +tracing-subscriber = { version = "0.3", features = ['time', 'json'] } +time = { version = "0.3", features = ['wasm-bindgen'] } diff --git a/examples/custom-req/package.json b/examples/custom-req/package.json index 48df609b..b3a05c8a 100644 --- a/examples/custom-req/package.json +++ b/examples/custom-req/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^2.13.0" + "wrangler": "^4" } } diff --git a/examples/custom-req/src/lib.rs b/examples/custom-req/src/lib.rs index 5ebad5f0..b5acc79c 100644 --- a/examples/custom-req/src/lib.rs +++ b/examples/custom-req/src/lib.rs @@ -34,6 +34,6 @@ impl IntoResponse for MyResponse { impl Into>, > { crate::worker_sys::web_sys::Response::new_with_opt_str(Some(self.data)) - .map_err(|e| Error::JsError(format!("{:?}", e))) + .map_err(|e| Error::JsError(format!("{e:?}"))) } } diff --git a/examples/custom-req/wrangler.toml b/examples/custom-req/wrangler.toml index e9e33f63..fe338654 100644 --- a/examples/custom-req/wrangler.toml +++ b/examples/custom-req/wrangler.toml @@ -1,6 +1,5 @@ name = "custom-req-workers" -main = "build/worker/shim.mjs" -compatibility_date = "2023-03-22" +main = "build/index.js" [build] command = "cargo install worker-build && worker-build --release" diff --git a/examples/custom-req/yarn.lock b/examples/custom-req/yarn.lock deleted file mode 100644 index 51068df9..00000000 --- a/examples/custom-req/yarn.lock +++ /dev/null @@ -1,870 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@cloudflare/kv-asset-handler@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz#c9959bbd7a1c40bd7c674adae98aa8c8d0e5ca68" - integrity sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A== - dependencies: - mime "^3.0.0" - -"@esbuild-plugins/node-globals-polyfill@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz#a313ab3efbb2c17c8ce376aa216c627c9b40f9d7" - integrity sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg== - -"@esbuild-plugins/node-modules-polyfill@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz#eb2f55da11967b2986c913f1a7957d1c868849c0" - integrity sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg== - dependencies: - escape-string-regexp "^4.0.0" - rollup-plugin-node-polyfills "^0.2.1" - -"@esbuild/android-arm64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.3.tgz#6af6d16be6d534d776a51fc215bfd81a68906d2c" - integrity sha512-RolFVeinkeraDvN/OoRf1F/lP0KUfGNb5jxy/vkIMeRRChkrX/HTYN6TYZosRJs3a1+8wqpxAo5PI5hFmxyPRg== - -"@esbuild/android-arm@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.3.tgz#2a091222f3b1928e3246fb3c5202eaca88baab67" - integrity sha512-mueuEoh+s1eRbSJqq9KNBQwI4QhQV6sRXIfTyLXSHGMpyew61rOK4qY21uKbXl1iBoMb0AdL1deWFCQVlN2qHA== - -"@esbuild/android-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.3.tgz#a6d749c58b022d371dc40d50ac1bb4aebd1eb953" - integrity sha512-SFpTUcIT1bIJuCCBMCQWq1bL2gPTjWoLZdjmIhjdcQHaUfV41OQfho6Ici5uvvkMmZRXIUGpM3GxysP/EU7ifQ== - -"@esbuild/darwin-arm64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.3.tgz#92d1826ed2f21dcac5830b70d7215c6afbb744e2" - integrity sha512-DO8WykMyB+N9mIDfI/Hug70Dk1KipavlGAecxS3jDUwAbTpDXj0Lcwzw9svkhxfpCagDmpaTMgxWK8/C/XcXvw== - -"@esbuild/darwin-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.3.tgz#7fc3570c2b16e9ff4fc178593a0a4adb1ae8ea57" - integrity sha512-uEqZQ2omc6BvWqdCiyZ5+XmxuHEi1SPzpVxXCSSV2+Sh7sbXbpeNhHIeFrIpRjAs0lI1FmA1iIOxFozKBhKgRQ== - -"@esbuild/freebsd-arm64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.3.tgz#16735ce16f8c9a4e7289e9e259aa01a8d9874307" - integrity sha512-nJansp3sSXakNkOD5i5mIz2Is/HjzIhFs49b1tjrPrpCmwgBmH9SSzhC/Z1UqlkivqMYkhfPwMw1dGFUuwmXhw== - -"@esbuild/freebsd-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.3.tgz#f4edd1464cb072799ed6b8ab5178478e71c13459" - integrity sha512-TfoDzLw+QHfc4a8aKtGSQ96Wa+6eimljjkq9HKR0rHlU83vw8aldMOUSJTUDxbcUdcgnJzPaX8/vGWm7vyV7ug== - -"@esbuild/linux-arm64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.3.tgz#4b7ae6fe3618d9a40d6ca39c6edc991ac1447203" - integrity sha512-7I3RlsnxEFCHVZNBLb2w7unamgZ5sVwO0/ikE2GaYvYuUQs9Qte/w7TqWcXHtCwxvZx/2+F97ndiUQAWs47ZfQ== - -"@esbuild/linux-arm@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.3.tgz#4b3e9f849822e16a76a70844c4db68075b259a58" - integrity sha512-VwswmSYwVAAq6LysV59Fyqk3UIjbhuc6wb3vEcJ7HEJUtFuLK9uXWuFoH1lulEbE4+5GjtHi3MHX+w1gNHdOWQ== - -"@esbuild/linux-ia32@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.3.tgz#2ff3936b91bfff62f9ecf7f6411ef399b29ed22d" - integrity sha512-X8FDDxM9cqda2rJE+iblQhIMYY49LfvW4kaEjoFbTTQ4Go8G96Smj2w3BRTwA8IHGoi9dPOPGAX63dhuv19UqA== - -"@esbuild/linux-loong64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.3.tgz#ff8aa59f49d9ccbc1ff952ba1f5cd01a534562df" - integrity sha512-hIbeejCOyO0X9ujfIIOKjBjNAs9XD/YdJ9JXAy1lHA+8UXuOqbFe4ErMCqMr8dhlMGBuvcQYGF7+kO7waj2KHw== - -"@esbuild/linux-mips64el@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.3.tgz#5dd5e118071c3912df69beedbfd11fb117f0fe5e" - integrity sha512-znFRzICT/V8VZQMt6rjb21MtAVJv/3dmKRMlohlShrbVXdBuOdDrGb+C2cZGQAR8RFyRe7HS6klmHq103WpmVw== - -"@esbuild/linux-ppc64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.3.tgz#36c62e24eae7fa3f0d921506da8fc1e6098a1364" - integrity sha512-EV7LuEybxhXrVTDpbqWF2yehYRNz5e5p+u3oQUS2+ZFpknyi1NXxr8URk4ykR8Efm7iu04//4sBg249yNOwy5Q== - -"@esbuild/linux-riscv64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.3.tgz#f0fec8e7affb5bcc817fefc61a21cbb95539e393" - integrity sha512-uDxqFOcLzFIJ+r/pkTTSE9lsCEaV/Y6rMlQjUI9BkzASEChYL/aSQjZjchtEmdnVxDKETnUAmsaZ4pqK1eE5BQ== - -"@esbuild/linux-s390x@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.3.tgz#22e10edd6e91f53c2e1f60e46abd453d7794409b" - integrity sha512-NbeREhzSxYwFhnCAQOQZmajsPYtX71Ufej3IQ8W2Gxskfz9DK58ENEju4SbpIj48VenktRASC52N5Fhyf/aliQ== - -"@esbuild/linux-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.3.tgz#38388b73fd9eebe45b073d7d8099b9c2e54f7139" - integrity sha512-SDiG0nCixYO9JgpehoKgScwic7vXXndfasjnD5DLbp1xltANzqZ425l7LSdHynt19UWOcDjG9wJJzSElsPvk0w== - -"@esbuild/netbsd-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.3.tgz#e0270569567f1530b8dbe6d11d5b4930b9cc71ae" - integrity sha512-AzbsJqiHEq1I/tUvOfAzCY15h4/7Ivp3ff/o1GpP16n48JMNAtbW0qui2WCgoIZArEHD0SUQ95gvR0oSO7ZbdA== - -"@esbuild/openbsd-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.3.tgz#3b16642d443848bca605f33ee3978a1890911e6d" - integrity sha512-gSABi8qHl8k3Cbi/4toAzHiykuBuWLZs43JomTcXkjMZVkp0gj3gg9mO+9HJW/8GB5H89RX/V0QP4JGL7YEEVg== - -"@esbuild/sunos-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.3.tgz#a838f247867380f0ae25ce1936dc5ab6f57b7734" - integrity sha512-SF9Kch5Ete4reovvRO6yNjMxrvlfT0F0Flm+NPoUw5Z4Q3r1d23LFTgaLwm3Cp0iGbrU/MoUI+ZqwCv5XJijCw== - -"@esbuild/win32-arm64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.3.tgz#bedd9bef5fb41f89ce2599f1761973cf6d6a67b6" - integrity sha512-u5aBonZIyGopAZyOnoPAA6fGsDeHByZ9CnEzyML9NqntK6D/xl5jteZUKm/p6nD09+v3pTM6TuUIqSPcChk5gg== - -"@esbuild/win32-ia32@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.3.tgz#49800aa812d8cc35ceef61e8d3b01224684cc0b1" - integrity sha512-GlgVq1WpvOEhNioh74TKelwla9KDuAaLZrdxuuUgsP2vayxeLgVc+rbpIv0IYF4+tlIzq2vRhofV+KGLD+37EQ== - -"@esbuild/win32-x64@0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.3.tgz#94047dae921949cfb308117d993c4b941291ae10" - integrity sha512-5/JuTd8OWW8UzEtyf19fbrtMJENza+C9JoPIkvItgTBQ1FO2ZLvjbPO6Xs54vk0s5JB5QsfieUEshRQfu7ZHow== - -"@fastify/busboy@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" - integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== - -"@iarna/toml@^2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" - integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== - -"@miniflare/cache@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/cache/-/cache-2.14.2.tgz#626d3eb899eeb850f3afd6bcdce452aa4234e333" - integrity sha512-XH218Y2jxSOfxG8EyuprBKhI/Fn6xLrb9A39niJBlzpiKXqr8skl/sy/sUL5tfvqEbEnqDagGne8zEcjM+1fBg== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - http-cache-semantics "^4.1.0" - undici "5.28.2" - -"@miniflare/cli-parser@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/cli-parser/-/cli-parser-2.14.2.tgz#8d9468e0b7a2f7d461995c9f2ed155afc49d2ee4" - integrity sha512-CzC7OnPWuMWSJrmnn0PUToMFDkMEnsFFE+ybA+Gqpgcdb/gaLz+yP0/Hagb5YN4JMxh5SBG7NCktsjZKaO94ig== - dependencies: - "@miniflare/shared" "2.14.2" - kleur "^4.1.4" - -"@miniflare/core@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/core/-/core-2.14.2.tgz#6a08738cf2f72ea60f3b92c5ce3fb974dbaf6122" - integrity sha512-n/smm5ZTg7ilGM4fxO7Gxhbe573oc8Za06M3b2fO+lPWqF6NJcEKdCC+sJntVFbn3Cbbd2G1ChISmugPfmlCkQ== - dependencies: - "@iarna/toml" "^2.2.5" - "@miniflare/queues" "2.14.2" - "@miniflare/shared" "2.14.2" - "@miniflare/watcher" "2.14.2" - busboy "^1.6.0" - dotenv "^10.0.0" - kleur "^4.1.4" - set-cookie-parser "^2.4.8" - undici "5.28.2" - urlpattern-polyfill "^4.0.3" - -"@miniflare/d1@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/d1/-/d1-2.14.2.tgz#0bb21db0a94e1a5ecffacc289da78aa65f02530a" - integrity sha512-3NPJyBLbFfzz9VAAdIZrDRdRpyslVCJoZHQk0/0CX3z2mJIfcQzjZhox2cYCFNH8NMJ7pRg6AeSMPYAnDKECDg== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - -"@miniflare/durable-objects@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/durable-objects/-/durable-objects-2.14.2.tgz#f94c86d0124e3476c67622add009a38c7f779552" - integrity sha512-BfK+ZkJABoi7gd/O6WbpsO4GrgW+0dmOBWJDlNBxQ7GIpa+w3n9+SNnrYUxKzWlPSvz+TfTTk381B1z/Z87lPw== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - "@miniflare/storage-memory" "2.14.2" - undici "5.28.2" - -"@miniflare/html-rewriter@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/html-rewriter/-/html-rewriter-2.14.2.tgz#1f699255a1f4b76f5118bb35f0cd346127b61bce" - integrity sha512-tu0kd9bj38uZ04loHb3sMI8kzUzZPgPOAJEdS9zmdSPh0uOkjCDf/TEkKsDdv2OFysyb0DRsIrwhPqCTIrPf1Q== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - html-rewriter-wasm "^0.4.1" - undici "5.28.2" - -"@miniflare/http-server@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/http-server/-/http-server-2.14.2.tgz#f59a3ab76e2938076afe137764d5ee9c163b4e6d" - integrity sha512-cc8OfZahdPd7pDER3xR1Io29g4pLrVhhxYnoT7t2TbhLoxOl93tRjxdUPX/UEjmy0MCYS4mutpSoWx49FB9OcA== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - "@miniflare/web-sockets" "2.14.2" - kleur "^4.1.4" - selfsigned "^2.0.0" - undici "5.28.2" - ws "^8.2.2" - youch "^2.2.2" - -"@miniflare/kv@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/kv/-/kv-2.14.2.tgz#cbca1feb23338106b7c7a815bd4d38e08e6476b0" - integrity sha512-3rs4cJOGACT/U7NH7j4KD29ugXRYUiM0aGkvOEdFQtChXLsYClJljXpezTfJJxBwZjdS4F2UFTixtFcHp74UfA== - dependencies: - "@miniflare/shared" "2.14.2" - -"@miniflare/queues@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/queues/-/queues-2.14.2.tgz#c20b76948a720b257b748c5611b84623f6d17e55" - integrity sha512-OylkRs4lOWKvGnX+Azab3nx+1qwC87M36/hkgAU1RRvVDCOxOrYLvNLUczFfgmgMBwpYsmmW8YOIASlI3p4Qgw== - dependencies: - "@miniflare/shared" "2.14.2" - -"@miniflare/r2@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/r2/-/r2-2.14.2.tgz#7d970045c680c74859aa491180fdd6dfa187d39d" - integrity sha512-uuc7dx6OqSQT8i0F2rsigfizXmInssRvvJAjoi1ltaNZNJCHH9l1PwHfaNc/XAuDjYmiCjtHDaPdRvZU9g9F3g== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - undici "5.28.2" - -"@miniflare/runner-vm@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/runner-vm/-/runner-vm-2.14.2.tgz#b108ee6ecdbdb84aaad94419b3be148df4cb3f18" - integrity sha512-WlyxAQ+bv/9Pm/xnbpgAg7RNX4pz/q3flytUoo4z4OrRmNEuXrbMUsJZnH8dziqzrZ2gCLkYIEzeaTmSQKp5Jg== - dependencies: - "@miniflare/shared" "2.14.2" - -"@miniflare/scheduler@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/scheduler/-/scheduler-2.14.2.tgz#013abe48c91c42c6ad7342789ea2a5e372d42824" - integrity sha512-gJejGz9F2hZl8NHfQd2iNwDnuNsK27DkWpLHiPkIqlrbz8tglN/kUKAa0rbTOKipsdo2+h6KRqLRq5PxvJ3T8w== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - cron-schedule "^3.0.4" - -"@miniflare/shared@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/shared/-/shared-2.14.2.tgz#ee49f2989a18b30747415dd1f90da7ae851fea90" - integrity sha512-dDnYIztz10zDQjaFJ8Gy9UaaBWZkw3NyhFdpX6tAeyPA/2lGvkftc42MYmNi8s5ljqkZAtKgWAJnSf2K75NCJw== - dependencies: - "@types/better-sqlite3" "^7.6.0" - kleur "^4.1.4" - npx-import "^1.1.4" - picomatch "^2.3.1" - -"@miniflare/sites@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/sites/-/sites-2.14.2.tgz#6b77f9cf0b6bf07f38c980e8e21c3dcfbd4fd867" - integrity sha512-jFOx1G5kD+kTubsga6jcFbMdU2nSuNG2/EkojwuhYT8hYp3qd8duvPyh1V+OR2tMvM4FWu6jXPXNZNBHXHQaUQ== - dependencies: - "@miniflare/kv" "2.14.2" - "@miniflare/shared" "2.14.2" - "@miniflare/storage-file" "2.14.2" - -"@miniflare/storage-file@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/storage-file/-/storage-file-2.14.2.tgz#d180b9f1a32f92497ac5993b008e9eddfba938e6" - integrity sha512-tn8rqMBeTtN+ICHQAMKQ0quHGYIkcyDK0qKW+Ic14gdfGDZx45BqXExQM9wTVqKtwAt85zp5eKVUYQCFfUx46Q== - dependencies: - "@miniflare/shared" "2.14.2" - "@miniflare/storage-memory" "2.14.2" - -"@miniflare/storage-memory@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/storage-memory/-/storage-memory-2.14.2.tgz#b4eac56ddeca8b839f10cce62914a7c843e44f70" - integrity sha512-9Wtz9mayHIY0LDsfpMGx+/sfKCq3eAQJzYY+ju1tTEaKR0sVAuO51wu0wbyldsjj9OcBcd2X32iPbIa7KcSZQQ== - dependencies: - "@miniflare/shared" "2.14.2" - -"@miniflare/watcher@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/watcher/-/watcher-2.14.2.tgz#619f2f6dff630eddf9f690b611cc9669fdedb900" - integrity sha512-/TL0np4uYDl+6MdseDApZmDdlJ6Y7AY5iDY0TvUQJG9nyBoCjX6w0Zn4SiKDwO6660rPtSqZ5c7HzbPhGb5vsA== - dependencies: - "@miniflare/shared" "2.14.2" - -"@miniflare/web-sockets@2.14.2": - version "2.14.2" - resolved "https://registry.yarnpkg.com/@miniflare/web-sockets/-/web-sockets-2.14.2.tgz#8a7a1eead842aa96e0632382d0807c141a29037b" - integrity sha512-kpbVlznPuxNQahssQvZiNPQo/iPme7qV3WMQeg6TYNCkYD7n6vEqeFZ5E/eQgB59xCanpvw4Ci8y/+SdMK6BUg== - dependencies: - "@miniflare/core" "2.14.2" - "@miniflare/shared" "2.14.2" - undici "5.28.2" - ws "^8.2.2" - -"@types/better-sqlite3@^7.6.0": - version "7.6.9" - resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz#4bff3eb7c5eaaae26f8099606c69279146561c50" - integrity sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ== - dependencies: - "@types/node" "*" - -"@types/node-forge@^1.3.0": - version "1.3.11" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" - integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "20.12.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.3.tgz#d6658c2c7776c1cad93534bb45428195ed840c65" - integrity sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw== - dependencies: - undici-types "~5.26.4" - -"@types/stack-trace@0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/stack-trace/-/stack-trace-0.0.29.tgz#eb7a7c60098edb35630ed900742a5ecb20cfcb4d" - integrity sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g== - -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -binary-extensions@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" - integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== - -blake3-wasm@^2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/blake3-wasm/-/blake3-wasm-2.1.5.tgz#b22dbb84bc9419ed0159caa76af4b1b132e6ba52" - integrity sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g== - -braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -builtins@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== - dependencies: - semver "^7.0.0" - -busboy@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - -chokidar@^3.5.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -cookie@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - -cron-schedule@^3.0.4: - version "3.0.6" - resolved "https://registry.yarnpkg.com/cron-schedule/-/cron-schedule-3.0.6.tgz#7d0a3ad9154112fc3720fe43238a43d50e8465e7" - integrity sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg== - -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - -esbuild@0.16.3: - version "0.16.3" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.3.tgz#5868632fa23f7a8547f2a4ea359c44e946515c94" - integrity sha512-71f7EjPWTiSguen8X/kxEpkAS7BFHwtQKisCDDV3Y4GLGWBaoSCyD5uXkaUew6JDzA9FEN1W23mdnSwW9kqCeg== - optionalDependencies: - "@esbuild/android-arm" "0.16.3" - "@esbuild/android-arm64" "0.16.3" - "@esbuild/android-x64" "0.16.3" - "@esbuild/darwin-arm64" "0.16.3" - "@esbuild/darwin-x64" "0.16.3" - "@esbuild/freebsd-arm64" "0.16.3" - "@esbuild/freebsd-x64" "0.16.3" - "@esbuild/linux-arm" "0.16.3" - "@esbuild/linux-arm64" "0.16.3" - "@esbuild/linux-ia32" "0.16.3" - "@esbuild/linux-loong64" "0.16.3" - "@esbuild/linux-mips64el" "0.16.3" - "@esbuild/linux-ppc64" "0.16.3" - "@esbuild/linux-riscv64" "0.16.3" - "@esbuild/linux-s390x" "0.16.3" - "@esbuild/linux-x64" "0.16.3" - "@esbuild/netbsd-x64" "0.16.3" - "@esbuild/openbsd-x64" "0.16.3" - "@esbuild/sunos-x64" "0.16.3" - "@esbuild/win32-arm64" "0.16.3" - "@esbuild/win32-ia32" "0.16.3" - "@esbuild/win32-x64" "0.16.3" - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -estree-walker@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" - integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== - -execa@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" - integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.1" - human-signals "^3.0.1" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^3.0.7" - strip-final-newline "^3.0.0" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -get-stream@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -html-rewriter-wasm@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz#235e3d96c1aa4bfd2182661ee13881e290ff5ff2" - integrity sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q== - -http-cache-semantics@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -human-signals@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" - integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -kleur@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" - integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -magic-string@^0.25.3: - version "0.25.9" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== - dependencies: - sourcemap-codec "^1.4.8" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -mime@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" - integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== - -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - -miniflare@2.14.2: - version "2.14.2" - resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-2.14.2.tgz#e0b0fdb7805c57cb15f7c8a24a7ad3b3d117944c" - integrity sha512-s2gruuOFT0o3cmQW+EX3k2EaLe1iHZ4OVXRmcNLj5ivrxxRHcK4Hy0LMd6bUOVDcT5KSKs7ylBkHqzp0zmUKCg== - dependencies: - "@miniflare/cache" "2.14.2" - "@miniflare/cli-parser" "2.14.2" - "@miniflare/core" "2.14.2" - "@miniflare/d1" "2.14.2" - "@miniflare/durable-objects" "2.14.2" - "@miniflare/html-rewriter" "2.14.2" - "@miniflare/http-server" "2.14.2" - "@miniflare/kv" "2.14.2" - "@miniflare/queues" "2.14.2" - "@miniflare/r2" "2.14.2" - "@miniflare/runner-vm" "2.14.2" - "@miniflare/scheduler" "2.14.2" - "@miniflare/shared" "2.14.2" - "@miniflare/sites" "2.14.2" - "@miniflare/storage-file" "2.14.2" - "@miniflare/storage-memory" "2.14.2" - "@miniflare/web-sockets" "2.14.2" - kleur "^4.1.4" - semiver "^1.1.0" - source-map-support "^0.5.20" - undici "5.28.2" - -mustache@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" - integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== - -nanoid@^3.3.3: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== - -node-forge@^1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" - integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== - dependencies: - path-key "^4.0.0" - -npx-import@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/npx-import/-/npx-import-1.1.4.tgz#0ee9a27484c633255528f7ec2e4c2adeaa1fcda3" - integrity sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA== - dependencies: - execa "^6.1.0" - parse-package-name "^1.0.0" - semver "^7.3.7" - validate-npm-package-name "^4.0.0" - -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== - dependencies: - mimic-fn "^4.0.0" - -parse-package-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-package-name/-/parse-package-name-1.0.0.tgz#1a108757e4ffc6889d5e78bcc4932a97c097a5a7" - integrity sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - -path-to-regexp@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" - integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rollup-plugin-inject@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz#e4233855bfba6c0c12a312fd6649dff9a13ee9f4" - integrity sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w== - dependencies: - estree-walker "^0.6.1" - magic-string "^0.25.3" - rollup-pluginutils "^2.8.1" - -rollup-plugin-node-polyfills@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz#53092a2744837164d5b8a28812ba5f3ff61109fd" - integrity sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA== - dependencies: - rollup-plugin-inject "^3.0.0" - -rollup-pluginutils@^2.8.1: - version "2.8.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" - integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== - dependencies: - estree-walker "^0.6.1" - -selfsigned@^2.0.0, selfsigned@^2.0.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" - integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== - dependencies: - "@types/node-forge" "^1.3.0" - node-forge "^1" - -semiver@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semiver/-/semiver-1.1.0.tgz#9c97fb02c21c7ce4fcf1b73e2c7a24324bdddd5f" - integrity sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg== - -semver@^7.0.0, semver@^7.3.7: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" - -set-cookie-parser@^2.4.8: - version "2.6.0" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" - integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -source-map-support@^0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" - integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== - -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -undici@5.28.2: - version "5.28.2" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.2.tgz#fea200eac65fc7ecaff80a023d1a0543423b4c91" - integrity sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w== - dependencies: - "@fastify/busboy" "^2.0.0" - -urlpattern-polyfill@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz#c1fa7a73eb4e6c6a1ffb41b24cf31974f7392d3b" - integrity sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ== - -validate-npm-package-name@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" - integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== - dependencies: - builtins "^5.0.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wrangler@^2.13.0: - version "2.21.1" - resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-2.21.1.tgz#e37fa196f6feb319f426a094444f675385f9cbb6" - integrity sha512-gPLqLHUvIR1TVLc2fARtIJ4UNVpTTMbxXKCZyLqGuLmbYPFQhvNKlct2eyfAYuYaTOr8g7VxswMn2mFab1Gu5A== - dependencies: - "@cloudflare/kv-asset-handler" "^0.2.0" - "@esbuild-plugins/node-globals-polyfill" "^0.1.1" - "@esbuild-plugins/node-modules-polyfill" "^0.1.4" - "@miniflare/core" "2.14.2" - "@miniflare/d1" "2.14.2" - "@miniflare/durable-objects" "2.14.2" - blake3-wasm "^2.1.5" - chokidar "^3.5.3" - esbuild "0.16.3" - miniflare "2.14.2" - nanoid "^3.3.3" - path-to-regexp "^6.2.0" - selfsigned "^2.0.1" - source-map "^0.7.4" - xxhash-wasm "^1.0.1" - optionalDependencies: - fsevents "~2.3.2" - -ws@^8.2.2: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== - -xxhash-wasm@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz#ecc0f813219b727af4d5f3958ca6becee2f2f1ff" - integrity sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -youch@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/youch/-/youch-2.2.2.tgz#cb87a359a5c524ebd35eb07ca3a1521dbc7e1a3e" - integrity sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ== - dependencies: - "@types/stack-trace" "0.0.29" - cookie "^0.4.1" - mustache "^4.2.0" - stack-trace "0.0.10" diff --git a/examples/digest/Cargo.toml b/examples/digest/Cargo.toml index 66353449..7bb4ee3d 100644 --- a/examples/digest/Cargo.toml +++ b/examples/digest/Cargo.toml @@ -6,13 +6,9 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { workspace=true } -hex = "0.4.3" +worker = { workspace = true } +hex = "0.4" diff --git a/examples/digest/package.json b/examples/digest/package.json index af40019b..a50e6baa 100644 --- a/examples/digest/package.json +++ b/examples/digest/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^3.52.0" + "wrangler": "^4" } } diff --git a/examples/digest/src/lib.rs b/examples/digest/src/lib.rs index a8bfb724..a558d268 100644 --- a/examples/digest/src/lib.rs +++ b/examples/digest/src/lib.rs @@ -29,9 +29,9 @@ async fn main(_req: Request, _env: Env, _ctx: Context) -> Result { } fn str_to_readable_stream(value: &str) -> web_sys::ReadableStream { - let mut req_init = web_sys::RequestInit::new(); - req_init.method("POST"); - req_init.body(Some(&JsValue::from_str(value))); + let req_init = web_sys::RequestInit::new(); + req_init.set_method("POST"); + req_init.set_body(&JsValue::from_str(value)); let req = web_sys::Request::new_with_str_and_init("http://internal/", &req_init).unwrap(); req.body().unwrap() } diff --git a/examples/digest/wrangler.toml b/examples/digest/wrangler.toml index 8873c600..151239e6 100644 --- a/examples/digest/wrangler.toml +++ b/examples/digest/wrangler.toml @@ -1,6 +1,5 @@ name = "digest-stream-on-workers" -main = "build/worker/shim.mjs" -compatibility_date = "2023-03-22" +main = "build/index.js" [build] command = "cargo install worker-build && worker-build --release" diff --git a/examples/fetcher/.gitignore b/examples/fetcher/.gitignore new file mode 100644 index 00000000..7708da3d --- /dev/null +++ b/examples/fetcher/.gitignore @@ -0,0 +1,4 @@ +/target +/build +/node_modules +/pkg diff --git a/examples/tokio-postgres/Cargo.lock b/examples/fetcher/Cargo.lock similarity index 100% rename from examples/tokio-postgres/Cargo.lock rename to examples/fetcher/Cargo.lock diff --git a/examples/fetcher/Cargo.toml b/examples/fetcher/Cargo.toml new file mode 100644 index 00000000..9b375aa0 --- /dev/null +++ b/examples/fetcher/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fetcher-on-workers" +version = "0.1.0" +edition = "2021" + +[package.metadata.release] +release = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +worker = { workspace = true } +serde = "1.0.188" +serde_json = "1.0.108" diff --git a/examples/fetcher/README.md b/examples/fetcher/README.md new file mode 100644 index 00000000..7cdce549 --- /dev/null +++ b/examples/fetcher/README.md @@ -0,0 +1,3 @@ +# `fetcher` on Cloudflare Workers + +Demonstration of fetching external API on Cloudflare Workers using `worker::Fetch`. diff --git a/examples/fetcher/src/lib.rs b/examples/fetcher/src/lib.rs new file mode 100644 index 00000000..0d77925e --- /dev/null +++ b/examples/fetcher/src/lib.rs @@ -0,0 +1,27 @@ +use worker::{event, Context, Env, Fetch, Request, Response, Result, RouteContext, Router, Url}; + +const POKEMON_API_URL: &str = "https://pokeapi.co/api/v2/pokemon"; + +/// Fetches Pokémon data from the PokeAPI given a full URL. +async fn fetch_pokemon(url_string: &str) -> Result { + // construct a new Url + let url = Url::parse(url_string)?; + Fetch::Url(url).send().await +} + +/// Route handler for GET /pokemon/:name +async fn get_pokemon(_: Request, ctx: RouteContext<()>) -> Result { + let name = ctx + .param("name") + .ok_or_else(|| worker::Error::RustError("Missing 'name' param".into()))?; + let url = format!("{POKEMON_API_URL}/{name}"); + fetch_pokemon(&url).await +} + +#[event(fetch)] +pub async fn main(req: Request, env: Env, _ctx: Context) -> Result { + Router::new() + .get_async("/pokemon/:name", get_pokemon) + .run(req, env) + .await +} diff --git a/examples/fetcher/wrangler.toml b/examples/fetcher/wrangler.toml new file mode 100644 index 00000000..7d04b407 --- /dev/null +++ b/examples/fetcher/wrangler.toml @@ -0,0 +1,6 @@ +name = "fetcher-on-workers" +main = "build/worker/shim.mjs" +compatibility_date = "2023-03-22" + +[build] +command = "cargo install worker-build && worker-build --release" diff --git a/examples/kv/Cargo.toml b/examples/kv/Cargo.toml index 4b442382..f3b35987 100644 --- a/examples/kv/Cargo.toml +++ b/examples/kv/Cargo.toml @@ -11,8 +11,6 @@ release = false crate-type = ["cdylib", "rlib"] [dependencies] -js-sys = { workspace = true } -serde_json = "1.0.64" -wasm-bindgen = { workspace = true } -wasm-bindgen-futures = { workspace = true } -worker-kv = { path = "../../worker-kv" } +serde_json.workspace = true +worker.workspace = true +worker-kv.workspace = true diff --git a/examples/kv/src/lib.rs b/examples/kv/src/lib.rs index a64f1008..2a545ad3 100644 --- a/examples/kv/src/lib.rs +++ b/examples/kv/src/lib.rs @@ -1,18 +1,14 @@ -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use worker_kv::*; +use worker::{event, Env, Request, Response, Result}; +use worker_kv::KvError; -async fn list() -> Result { - let kv = KvStore::create("EXAMPLE")?; - let list_response = kv.list().limit(100).execute().await?; - - // Returns a pretty printed version of the listed key value pairs. - serde_json::to_string_pretty(&list_response) - .map(Into::into) - .map_err(Into::into) -} - -#[wasm_bindgen] -pub fn start() -> Promise { - wasm_bindgen_futures::future_to_promise(async { list().await.map_err(Into::into) }) +#[event(fetch)] +async fn main(_req: Request, env: Env, _: worker::Context) -> Result { + let kv = env.kv("EXAMPLE")?; + let list_response = kv.list().limit(100).execute().await.map_err(|e| { + if matches!(e, KvError::InvalidKvStore(_)) { + panic!("invalid kv store"); + } + e + })?; + Response::from_html(serde_json::to_string_pretty(&list_response)?) } diff --git a/examples/kv/wrangler.example.toml b/examples/kv/wrangler.example.toml deleted file mode 100644 index 67b41f20..00000000 --- a/examples/kv/wrangler.example.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "example" -type = "rust" -# Enter your account id -account_id = "" -workers_dev = true -route = "" -zone_id = "" - -# The KV name spaces we will be using in the example -kv_namespaces = [ - { binding = "EXAMPLE", id = "", preview_id = "" }, -] diff --git a/examples/queue/Cargo.toml b/examples/queue/Cargo.toml index ca6f06c9..21c4308e 100644 --- a/examples/queue/Cargo.toml +++ b/examples/queue/Cargo.toml @@ -6,15 +6,10 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] serde = "1" worker = { workspace = true, features = ["queue"] } -wasm-bindgen = { workspace = true } -js-sys = { workspace = true } +js-sys.workspace = true diff --git a/examples/queue/package.json b/examples/queue/package.json index 381eaa1d..3ecd36bc 100644 --- a/examples/queue/package.json +++ b/examples/queue/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^3" + "wrangler": "^4" } } \ No newline at end of file diff --git a/examples/queue/wrangler.toml b/examples/queue/wrangler.toml index 40f487f1..d3029c65 100644 --- a/examples/queue/wrangler.toml +++ b/examples/queue/wrangler.toml @@ -1,10 +1,8 @@ name = "queue-on-workers" -main = "build/worker/shim.mjs" -compatibility_date = "2024-03-26" - +main = "build/index.js" [build] -command = "cargo install --path ../../worker-build && worker-build --release" +command = "cargo install worker-build && worker-build --release" [[queues.consumers]] queue = "mymessages" diff --git a/examples/router/Cargo.toml b/examples/router/Cargo.toml index 1df1a90f..e38f03a9 100644 --- a/examples/router/Cargo.toml +++ b/examples/router/Cargo.toml @@ -6,16 +6,12 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -serde = "1.0.188" -worker = { workspace=true } +serde = "1" +worker.workspace = true [dev-dependencies] tokio = { version = "1", features = ["full"] } diff --git a/examples/router/package.json b/examples/router/package.json index dbd9eddd..6b439c73 100644 --- a/examples/router/package.json +++ b/examples/router/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^2.13.0" + "wrangler": "^4" } } diff --git a/examples/router/wrangler.toml b/examples/router/wrangler.toml index b8d164d2..0d3d960e 100644 --- a/examples/router/wrangler.toml +++ b/examples/router/wrangler.toml @@ -1,6 +1,5 @@ name = "router-on-workers" -main = "build/worker/shim.mjs" -compatibility_date = "2023-03-22" +main = "build/index.js" [build] -command = "cargo install --path ../../worker-build && worker-build --release" +command = "cargo install worker-build && worker-build --release" diff --git a/examples/rpc-client/Cargo.toml b/examples/rpc-client/Cargo.toml index e295b6ac..d637afbc 100644 --- a/examples/rpc-client/Cargo.toml +++ b/examples/rpc-client/Cargo.toml @@ -6,16 +6,11 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { path="../../worker" } -console_error_panic_hook = { version = "0.1.1" } +worker.workspace = true # TODO: Remove the need for these async-trait = "0.1" @@ -23,4 +18,4 @@ wasm-bindgen = "0.2" serde-wasm-bindgen = "0.6" [build-dependencies] -worker-codegen = { path = "../../worker-codegen" } +worker-codegen.workspace = true diff --git a/examples/rpc-client/build.rs b/examples/rpc-client/build.rs index cf7919b8..2fe33006 100644 --- a/examples/rpc-client/build.rs +++ b/examples/rpc-client/build.rs @@ -7,7 +7,7 @@ fn main() -> Result<(), Box> { println!("cargo::rerun-if-changed=wit/calculator.wit"); let source = expand_wit_source("wit/calculator.wit")?; let out_dir = var("OUT_DIR")?; - let dest = format!("{}/calculator.rs", out_dir); + let dest = format!("{out_dir}/calculator.rs"); std::fs::write(dest, source)?; Ok(()) } diff --git a/examples/rpc-client/package.json b/examples/rpc-client/package.json index eb80692a..c6109a26 100644 --- a/examples/rpc-client/package.json +++ b/examples/rpc-client/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", + "deploy": "cargo install worker-build ; wrangler deploy", "dev": "wrangler dev" }, "devDependencies": { - "wrangler": "^3.0.0" + "wrangler": "^4.20.0" } } diff --git a/examples/rpc-client/src/lib.rs b/examples/rpc-client/src/lib.rs index 829d7466..438492d5 100644 --- a/examples/rpc-client/src/lib.rs +++ b/examples/rpc-client/src/lib.rs @@ -14,5 +14,5 @@ async fn main(_req: Request, env: Env, _ctx: Context) -> Result { let num = service.add(1, 2).await?; - Response::ok(format!("{:?}", num)) + Response::ok(format!("{num:?}")) } diff --git a/examples/rpc-client/wrangler.toml b/examples/rpc-client/wrangler.toml index ebe80cdb..df2fabde 100644 --- a/examples/rpc-client/wrangler.toml +++ b/examples/rpc-client/wrangler.toml @@ -1,5 +1,5 @@ name = "rust-rpc-client" -main = "build/worker/shim.mjs" +main = "build/index.js" compatibility_date = "2024-04-05" services = [ @@ -7,4 +7,4 @@ services = [ ] [build] -command = "cargo install --path ../../worker-build && worker-build --release" \ No newline at end of file +command = "cargo install worker-build && worker-build --release" \ No newline at end of file diff --git a/examples/rpc-server/Cargo.toml b/examples/rpc-server/Cargo.toml index 56d833e1..4b25bbf2 100644 --- a/examples/rpc-server/Cargo.toml +++ b/examples/rpc-server/Cargo.toml @@ -6,14 +6,8 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { path="../../worker" } -wasm-bindgen="0.2" -console_error_panic_hook = { version = "0.1.1" } +worker.workspace = true diff --git a/examples/rpc-server/package.json b/examples/rpc-server/package.json index b7d9cf42..0c4db73e 100644 --- a/examples/rpc-server/package.json +++ b/examples/rpc-server/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", + "deploy": "cargo install worker-build ; wrangler deploy", "dev": "wrangler dev" }, "devDependencies": { - "wrangler": "^3.0.0" + "wrangler": "^4.20.0" } } diff --git a/examples/rpc-server/src/lib.rs b/examples/rpc-server/src/lib.rs index 64f08c8a..b21f47d8 100644 --- a/examples/rpc-server/src/lib.rs +++ b/examples/rpc-server/src/lib.rs @@ -8,6 +8,5 @@ async fn main(_req: Request, _env: Env, _ctx: Context) -> Result { #[wasm_bindgen] pub async fn add(a: u32, b: u32) -> u32 { - console_error_panic_hook::set_once(); a + b } diff --git a/examples/rpc-server/wrangler.toml b/examples/rpc-server/wrangler.toml index 8b361de9..7c13dc27 100644 --- a/examples/rpc-server/wrangler.toml +++ b/examples/rpc-server/wrangler.toml @@ -1,6 +1,6 @@ name = "rust-rpc-server" -main = "build/worker/shim.mjs" +main = "build/index.js" compatibility_date = "2024-04-05" [build] -command = "cargo install --path ../../worker-build && worker-build --release" +command = "cargo install worker-build && worker-build --release" diff --git a/examples/tokio-postgres/Cargo.toml b/examples/tokio-postgres/Cargo.toml index d4f3e3c1..4249278a 100644 --- a/examples/tokio-postgres/Cargo.toml +++ b/examples/tokio-postgres/Cargo.toml @@ -7,14 +7,12 @@ resolver = "2" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { workspace=true, features=["tokio-postgres"] } -tokio-postgres = { version="0.7", features=['js'], default-features=false } -anyhow="1" +worker = { workspace = true, features = ["tokio-postgres"] } +tokio-postgres = { version = "0.7", features = [ + 'js', +], default-features = false } +anyhow = "1" diff --git a/examples/tokio-postgres/package.json b/examples/tokio-postgres/package.json index e74ab33b..3ccd827f 100644 --- a/examples/tokio-postgres/package.json +++ b/examples/tokio-postgres/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^2.13.0" + "wrangler": "^4" } } diff --git a/examples/tokio-postgres/wrangler.toml b/examples/tokio-postgres/wrangler.toml index 40fa60bc..1894c2cf 100644 --- a/examples/tokio-postgres/wrangler.toml +++ b/examples/tokio-postgres/wrangler.toml @@ -1,10 +1,9 @@ name = "hyperdrive-test" -main = "build/worker/shim.mjs" -compatibility_date = "2024-05-01" +main = "build/index.js" [[hyperdrive]] binding = "DB" id = "" [build] -command = "cargo install --path ../../worker-build && worker-build --release" \ No newline at end of file +command = "cargo install worker-build && worker-build --release" diff --git a/examples/tracing/Cargo.lock b/examples/tracing/Cargo.lock deleted file mode 100644 index 794dcaf0..00000000 --- a/examples/tracing/Cargo.lock +++ /dev/null @@ -1,884 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" -dependencies = [ - "js-sys", - "num-integer", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "chrono-tz" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c39203181991a7dd4343b8005bd804e7a9a37afb8ac070e43771e8c820bbde" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f509c3a87b33437b05e2458750a0700e5bdd6956176773e6c7d6dd15a283a0c" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matchit" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "parse-zoneinfo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" -dependencies = [ - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "phf" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" -dependencies = [ - "siphasher", - "uncased", -] - -[[package]] -name = "pin-project" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "proc-macro2" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "regex" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "serde" -version = "1.0.163" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_derive" -version = "1.0.163" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "serde_json" -version = "1.0.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" -dependencies = [ - "autocfg", - "bytes", - "pin-project-lite", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "uncased" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "wasm-streams" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "worker" -version = "0.0.16" -dependencies = [ - "async-trait", - "chrono", - "chrono-tz", - "futures-channel", - "futures-util", - "http", - "js-sys", - "matchit", - "pin-project", - "serde", - "serde-wasm-bindgen", - "serde_json", - "tokio", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "worker-kv", - "worker-macros", - "worker-sys", -] - -[[package]] -name = "worker-kv" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4b9fe1a87b7aef252fceb4f30bf6303036a5de329c81ccad9be9c35d1fdbc7" -dependencies = [ - "js-sys", - "serde", - "serde-wasm-bindgen", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "worker-macros" -version = "0.0.8" -dependencies = [ - "async-trait", - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-macro-support", - "worker-sys", -] - -[[package]] -name = "worker-rust" -version = "0.1.0" -dependencies = [ - "console_error_panic_hook", - "hyper", - "tokio", - "wasm-bindgen-futures", - "worker", -] - -[[package]] -name = "worker-sys" -version = "0.0.8" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] diff --git a/examples/tracing/Cargo.toml b/examples/tracing/Cargo.toml index b3738dcf..9108ecfb 100644 --- a/examples/tracing/Cargo.toml +++ b/examples/tracing/Cargo.toml @@ -6,16 +6,12 @@ edition = "2021" [package.metadata.release] release = false -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - [lib] crate-type = ["cdylib"] [dependencies] -worker = { workspace=true } +worker.workspace = true tracing = "0.1" tracing-web = "0.1" -tracing-subscriber = { version = "0.3", features=['time', 'json'] } -time = { version = "0.3", features=['wasm-bindgen'] } +tracing-subscriber = { version = "0.3", features = ['time', 'json'] } +time = { version = "0.3", features = ['wasm-bindgen'] } diff --git a/examples/tracing/package.json b/examples/tracing/package.json index 74af43d9..7b64e64f 100644 --- a/examples/tracing/package.json +++ b/examples/tracing/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --local" + "deploy": "cargo install worker-build ; wrangler deploy", + "dev": "cargo install worker-build ; wrangler dev --local" }, "devDependencies": { - "wrangler": "^2.13.0" + "wrangler": "^4" } } diff --git a/examples/tracing/wrangler.toml b/examples/tracing/wrangler.toml index 20227cd1..e65e32b5 100644 --- a/examples/tracing/wrangler.toml +++ b/examples/tracing/wrangler.toml @@ -1,6 +1,5 @@ name = "tracing-on-workers" -main = "build/worker/shim.mjs" -compatibility_date = "2023-03-22" +main = "build/index.js" [build] command = "cargo install worker-build && worker-build --release" diff --git a/package-lock.json b/package-lock.json index 89c0699c..376a21d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,73 +10,38 @@ "license": "MIT OR Apache-2.0", "dependencies": { "@changesets/changelog-github": "^0.4.8", - "@changesets/cli": "^2.26.2", - "@cloudflare/workers-types": "^4.20230807.0" + "@changesets/cli": "^2.29.6", + "@cloudflare/workers-types": "^4.20250826.0" }, "devDependencies": { - "@types/node": "^20.4.8", - "@types/uuid": "^9.0.2", - "miniflare": "^3.20231030.2", - "typescript": "^5.1.6", - "undici": "^5.28.4", - "uuid": "^9.0.0", - "vitest": "^0.32.4" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", - "dependencies": { - "@babel/highlight": "^7.22.10", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" + "@types/node": "^24.0.1", + "miniflare": "^4.20250923.0", + "typescript": "^5.8.3", + "undici": "7.14.0", + "uuid": "^11.1.0", + "vitest": "^3.2.4" } }, "node_modules/@babel/runtime": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", - "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@changesets/apply-release-plan": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.1.4.tgz", - "integrity": "sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==", - "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/config": "^2.3.1", - "@changesets/get-version-range-type": "^0.3.2", - "@changesets/git": "^2.0.0", - "@changesets/types": "^5.2.1", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.13.tgz", + "integrity": "sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==", + "license": "MIT", + "dependencies": { + "@changesets/config": "^3.1.1", + "@changesets/get-version-range-type": "^0.4.0", + "@changesets/git": "^3.0.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", @@ -87,31 +52,52 @@ "semver": "^7.5.3" } }, + "node_modules/@changesets/apply-release-plan/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/assemble-release-plan": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.2.4.tgz", - "integrity": "sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==", - "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/errors": "^0.1.4", - "@changesets/get-dependents-graph": "^1.3.6", - "@changesets/types": "^5.2.1", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.9.tgz", + "integrity": "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==", + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, + "node_modules/@changesets/assemble-release-plan/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/changelog-git": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.1.14.tgz", - "integrity": "sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", + "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", + "license": "MIT", "dependencies": { - "@changesets/types": "^5.2.1" + "@changesets/types": "^6.1.0" } }, + "node_modules/@changesets/changelog-git/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/changelog-github": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/@changesets/changelog-github/-/changelog-github-0.4.8.tgz", "integrity": "sha512-jR1DHibkMAb5v/8ym77E4AMNWZKB5NPzw5a5Wtqm1JepAuIF+hrKp2u04NKM14oBZhHglkCfrla9uq8ORnK/dw==", + "license": "MIT", "dependencies": { "@changesets/get-github-info": "^0.5.2", "@changesets/types": "^5.2.1", @@ -119,193 +105,260 @@ } }, "node_modules/@changesets/cli": { - "version": "2.26.2", - "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.26.2.tgz", - "integrity": "sha512-dnWrJTmRR8bCHikJHl9b9HW3gXACCehz4OasrXpMp7sx97ECuBGGNjJhjPhdZNCvMy9mn4BWdplI323IbqsRig==", - "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/apply-release-plan": "^6.1.4", - "@changesets/assemble-release-plan": "^5.2.4", - "@changesets/changelog-git": "^0.1.14", - "@changesets/config": "^2.3.1", - "@changesets/errors": "^0.1.4", - "@changesets/get-dependents-graph": "^1.3.6", - "@changesets/get-release-plan": "^3.0.17", - "@changesets/git": "^2.0.0", - "@changesets/logger": "^0.0.5", - "@changesets/pre": "^1.0.14", - "@changesets/read": "^0.5.9", - "@changesets/types": "^5.2.1", - "@changesets/write": "^0.2.3", + "version": "2.29.7", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.29.7.tgz", + "integrity": "sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ==", + "license": "MIT", + "dependencies": { + "@changesets/apply-release-plan": "^7.0.13", + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/changelog-git": "^0.2.1", + "@changesets/config": "^3.1.1", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/get-release-plan": "^4.0.13", + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.5", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@changesets/write": "^0.4.0", + "@inquirer/external-editor": "^1.0.0", "@manypkg/get-packages": "^1.1.3", - "@types/is-ci": "^3.0.0", - "@types/semver": "^7.5.0", "ansi-colors": "^4.1.3", - "chalk": "^2.1.0", - "enquirer": "^2.3.0", - "external-editor": "^3.1.0", + "ci-info": "^3.7.0", + "enquirer": "^2.4.1", "fs-extra": "^7.0.1", - "human-id": "^1.0.2", - "is-ci": "^3.0.1", - "meow": "^6.0.0", - "outdent": "^0.5.0", + "mri": "^1.2.0", "p-limit": "^2.2.0", - "preferred-pm": "^3.0.0", + "package-manager-detector": "^0.2.0", + "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", - "spawndamnit": "^2.0.0", - "term-size": "^2.1.0", - "tty-table": "^4.1.5" + "spawndamnit": "^3.0.1", + "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, + "node_modules/@changesets/cli/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/config": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@changesets/config/-/config-2.3.1.tgz", - "integrity": "sha512-PQXaJl82CfIXddUOppj4zWu+987GCw2M+eQcOepxN5s+kvnsZOwjEJO3DH9eVy+OP6Pg/KFEWdsECFEYTtbg6w==", - "dependencies": { - "@changesets/errors": "^0.1.4", - "@changesets/get-dependents-graph": "^1.3.6", - "@changesets/logger": "^0.0.5", - "@changesets/types": "^5.2.1", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.1.tgz", + "integrity": "sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==", + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/logger": "^0.1.1", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", - "micromatch": "^4.0.2" + "micromatch": "^4.0.8" } }, + "node_modules/@changesets/config/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/errors": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.1.4.tgz", - "integrity": "sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", + "license": "MIT", "dependencies": { "extendable-error": "^0.1.5" } }, "node_modules/@changesets/get-dependents-graph": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.6.tgz", - "integrity": "sha512-Q/sLgBANmkvUm09GgRsAvEtY3p1/5OCzgBE5vX3vgb5CvW0j7CEljocx5oPXeQSNph6FXulJlXV3Re/v3K3P3Q==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.3.tgz", + "integrity": "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==", + "license": "MIT", "dependencies": { - "@changesets/types": "^5.2.1", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", - "chalk": "^2.1.0", - "fs-extra": "^7.0.1", + "picocolors": "^1.1.0", "semver": "^7.5.3" } }, + "node_modules/@changesets/get-dependents-graph/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/get-github-info": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@changesets/get-github-info/-/get-github-info-0.5.2.tgz", "integrity": "sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg==", + "license": "MIT", "dependencies": { "dataloader": "^1.4.0", "node-fetch": "^2.5.0" } }, "node_modules/@changesets/get-release-plan": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.17.tgz", - "integrity": "sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==", - "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/assemble-release-plan": "^5.2.4", - "@changesets/config": "^2.3.1", - "@changesets/pre": "^1.0.14", - "@changesets/read": "^0.5.9", - "@changesets/types": "^5.2.1", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.13.tgz", + "integrity": "sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==", + "license": "MIT", + "dependencies": { + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/config": "^3.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.5", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, + "node_modules/@changesets/get-release-plan/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/get-version-range-type": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz", - "integrity": "sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", + "license": "MIT" }, "node_modules/@changesets/git": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@changesets/git/-/git-2.0.0.tgz", - "integrity": "sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", + "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/errors": "^0.1.4", - "@changesets/types": "^5.2.1", + "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", - "micromatch": "^4.0.2", - "spawndamnit": "^2.0.0" + "micromatch": "^4.0.8", + "spawndamnit": "^3.0.1" } }, "node_modules/@changesets/logger": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.0.5.tgz", - "integrity": "sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", + "license": "MIT", "dependencies": { - "chalk": "^2.1.0" + "picocolors": "^1.1.0" } }, "node_modules/@changesets/parse": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.16.tgz", - "integrity": "sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.1.tgz", + "integrity": "sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==", + "license": "MIT", "dependencies": { - "@changesets/types": "^5.2.1", + "@changesets/types": "^6.1.0", "js-yaml": "^3.13.1" } }, + "node_modules/@changesets/parse/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/pre": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.14.tgz", - "integrity": "sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", + "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/errors": "^0.1.4", - "@changesets/types": "^5.2.1", + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, + "node_modules/@changesets/pre/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/read": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.5.9.tgz", - "integrity": "sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==", - "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/git": "^2.0.0", - "@changesets/logger": "^0.0.5", - "@changesets/parse": "^0.3.16", - "@changesets/types": "^5.2.1", - "chalk": "^2.1.0", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.5.tgz", + "integrity": "sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==", + "license": "MIT", + "dependencies": { + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/parse": "^0.4.1", + "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", - "p-filter": "^2.1.0" + "p-filter": "^2.1.0", + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/read/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, + "node_modules/@changesets/should-skip-package": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", + "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" } }, + "node_modules/@changesets/should-skip-package/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@changesets/types": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/@changesets/types/-/types-5.2.1.tgz", - "integrity": "sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==" + "integrity": "sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==", + "license": "MIT" }, "node_modules/@changesets/write": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.2.3.tgz", - "integrity": "sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", + "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/types": "^5.2.1", + "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", - "human-id": "^1.0.2", + "human-id": "^4.1.1", "prettier": "^2.7.1" } }, + "node_modules/@changesets/write/node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "license": "MIT" + }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240320.1.tgz", - "integrity": "sha512-ioG5k2M17xyiAlK/k3L21NZLMVeSHMjwlmGtZyCyzSLL5/zGINcgZ5yPLV0UuWiysw07/6Jjzm5Sx94hzMVybg==", + "version": "1.20250923.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250923.0.tgz", + "integrity": "sha512-CUyVkdTaREdT/wynh5/VX3prawWpYeoqGjcEyo920/HqXaRuA/owp9ijg1vh1rmHyxN0XvsjHuRwBfnrptGmrg==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -315,13 +368,14 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240320.1.tgz", - "integrity": "sha512-Ga6RDdnFEIsN4WuWsaP9bLGvK9K7pEIVoSIgmw6vweVlD8UK/a2MPGrsF1ogwdeCTCOMY8wUh9poL/Yu48IPpg==", + "version": "1.20250923.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250923.0.tgz", + "integrity": "sha512-wblU5WYlNRnrTMupeWFRoysJH/Y7d6h+Wc1G+GTmaMV6TcxyXj804Hk8Tk3jqvaS0SXmkh5sIQ38MBVrBs7sag==", "cpu": [ "arm64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -331,13 +385,14 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240320.1.tgz", - "integrity": "sha512-KFof5H8eU0NXv+pUAU7Lk/OLtOmfsioTJqu0v6kPL7QsTGsgzj5sEQNcQ8DONSze549Yflu5W00qpA2cPz9eWQ==", + "version": "1.20250923.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250923.0.tgz", + "integrity": "sha512-WFP0KBJWhdDJWChIw3HJmrtYLNQDB8X9R3o548FcE5NiD05J0rI5Pnhno008lanjmXzia1lghWIQErfpjpmQzg==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -347,13 +402,14 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240320.1.tgz", - "integrity": "sha512-t+kGc6dGdkKvVMGcHCPhlCsUZF5dj8xbAFvLB7DAJ8T79ys30rmY2Lu/C8vKlhjH9TJhbzgKmPaJ0wC/K4euvw==", + "version": "1.20250923.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250923.0.tgz", + "integrity": "sha512-h5VegEhn9CPfGnTc4JTFLGzR806naeIcKUHWi8ejc1hO38YthC6mBNsOjbZrSkp4B3H/kstuhuW16x2rGX0oBg==", "cpu": [ "arm64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -363,13 +419,14 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240320.1.tgz", - "integrity": "sha512-9xDylCOsuzWqGuANkuUByiJ5RHeMqgw37FiI7rn8I6zdGAc/alOB9B4Bh7B73WC2uEpFL+XCEjcHZ6NmsO4NaQ==", + "version": "1.20250923.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250923.0.tgz", + "integrity": "sha512-S1E2Vm11ClrwHLqbVO59pKpxllHkq3APRdt3wCbVZNG7jGctxEYLH2uE0K1O1JBfbk2fKMaa+sbmtMpXDXi1tw==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "win32" @@ -379,15 +436,17 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "4.20230807.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20230807.0.tgz", - "integrity": "sha512-gQczWuGE2rxmpzOCNn0zLbx8Xz0gqspdE9S7tu4Xax39q1csgO/E9flcS+KG3GHB522ugOh84inmABDhpeJnvQ==" + "version": "4.20250923.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20250923.0.tgz", + "integrity": "sha512-EMs5wF70f9Dt9QtxX/lSyaZIcMdcDP0VhXOW0u5HrDaIHeXDjPJw76W1lkoGF1x8XAVclhiD5zGJZTdZnoXJiQ==", + "license": "MIT OR Apache-2.0" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -395,442 +454,930 @@ "node": ">=12" } }, + "node_modules/@emnapi/runtime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=14" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" } }, - "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], "dev": true, - "engines": { - "node": ">=6.0.0" + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@manypkg/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "@types/node": "^12.7.1", - "find-up": "^4.1.0", - "fs-extra": "^8.1.0" + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@manypkg/find-root/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - }, - "node_modules/@manypkg/find-root/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@manypkg/get-packages": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", - "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "@changesets/types": "^4.0.1", - "@manypkg/find-root": "^1.1.0", - "fs-extra": "^8.1.0", + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", + "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + } + }, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "license": "MIT" + }, + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@manypkg/get-packages": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@changesets/types": "^4.0.1", + "@manypkg/find-root": "^1.1.0", + "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } @@ -838,12 +1385,14 @@ "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", - "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==" + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", + "license": "MIT" }, "node_modules/@manypkg/get-packages/node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -857,6 +1406,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -869,6 +1419,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -877,6 +1428,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -885,150 +1437,518 @@ "node": ">= 8" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "node_modules/@poppinss/colors": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.5.tgz", + "integrity": "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.4.tgz", + "integrity": "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.2.tgz", + "integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.2.tgz", + "integrity": "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.2.tgz", + "integrity": "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.2.tgz", + "integrity": "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.2.tgz", + "integrity": "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.2.tgz", + "integrity": "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.2.tgz", + "integrity": "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.2.tgz", + "integrity": "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.2.tgz", + "integrity": "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.2.tgz", + "integrity": "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.2.tgz", + "integrity": "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.2.tgz", + "integrity": "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.2.tgz", + "integrity": "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.2.tgz", + "integrity": "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.2.tgz", + "integrity": "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.2.tgz", + "integrity": "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.2.tgz", + "integrity": "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.2.tgz", + "integrity": "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.2.tgz", + "integrity": "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.2.tgz", + "integrity": "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.2.tgz", + "integrity": "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.2.tgz", + "integrity": "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz", + "integrity": "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/is": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.0.tgz", + "integrity": "sha512-7F/yz2IphV39hiS2zB4QYVkivrptHHh0K8qJJd9HhuWSdvf8AN7NpebW3CcDZDBQsUPMoDKWsY2WWgW7bqOcfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@speed-highlight/core": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.7.tgz", + "integrity": "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", - "dev": true - }, - "node_modules/@types/chai-subset": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", - "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/chai": "*" + "@types/deep-eql": "*" } }, - "node_modules/@types/is-ci": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/is-ci/-/is-ci-3.0.0.tgz", - "integrity": "sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==", - "dependencies": { - "ci-info": "^3.1.0" - } + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.4.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.8.tgz", - "integrity": "sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" - }, - "node_modules/@types/uuid": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", - "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", - "dev": true + "version": "24.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", + "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.12.0" + } }, "node_modules/@vitest/expect": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.4.tgz", - "integrity": "sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/spy": "0.32.4", - "@vitest/utils": "0.32.4", - "chai": "^4.3.7" + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.4.tgz", - "integrity": "sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==", + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/utils": "0.32.4", - "p-limit": "^4.0.0", - "pathe": "^1.1.1" + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, + "license": "MIT", "dependencies": { - "yocto-queue": "^1.0.0" + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.4.tgz", - "integrity": "sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, + "license": "MIT", "dependencies": { - "magic-string": "^0.30.0", - "pathe": "^1.1.1", - "pretty-format": "^29.5.0" + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.4.tgz", - "integrity": "sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, + "license": "MIT", "dependencies": { - "tinyspy": "^2.1.1" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.4.tgz", - "integrity": "sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, + "license": "MIT", "dependencies": { - "diff-sequences": "^29.4.3", - "loupe": "^2.3.6", - "pretty-format": "^29.5.0" + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1037,10 +1957,11 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -1049,6 +1970,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -1057,126 +1979,44 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/as-table": { - "version": "1.0.55", - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", - "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", - "dev": true, - "dependencies": { - "printable-characters": "^1.0.42" - } - }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, + "license": "MIT", "engines": { - "node": "*" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, "node_modules/better-path-resolve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "license": "MIT", "dependencies": { "is-windows": "^1.0.0" }, @@ -1188,6 +2028,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -1195,242 +2036,147 @@ "node": ">=8" } }, - "node_modules/breakword": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/breakword/-/breakword-1.0.6.tgz", - "integrity": "sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==", - "dependencies": { - "wcwidth": "^1.0.1" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/capnp-ts": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/capnp-ts/-/capnp-ts-0.7.0.tgz", - "integrity": "sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==", - "dev": true, - "dependencies": { - "debug": "^4.3.1", - "tslib": "^2.2.0" - } - }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, + "license": "MIT", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=18" } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "license": "MIT" }, "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "color-convert": "^2.0.1", + "color-string": "^1.9.0" }, "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" + "node": ">=12.5.0" } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "engines": { - "node": ">= 0.6" - } + "license": "MIT" }, - "node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/cross-spawn/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" } }, - "node_modules/cross-spawn/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, - "node_modules/csv": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/csv/-/csv-5.5.3.tgz", - "integrity": "sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { - "csv-generate": "^3.4.3", - "csv-parse": "^4.16.3", - "csv-stringify": "^5.6.5", - "stream-transform": "^2.1.3" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">= 0.1.90" + "node": ">= 8" } }, - "node_modules/csv-generate": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.4.3.tgz", - "integrity": "sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==" - }, - "node_modules/csv-parse": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", - "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==" - }, - "node_modules/csv-stringify": { - "version": "5.6.5", - "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", - "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" - }, - "node_modules/data-uri-to-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", - "dev": true - }, "node_modules/dataloader": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", - "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==" + "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==", + "license": "BSD-3-Clause" }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1441,96 +2187,40 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -1542,19 +2232,16 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "license": "BSD-2-Clause", "engines": { "node": ">=10" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -1563,160 +2250,70 @@ "node": ">=8.6" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dependencies": { - "has": "^1.0.3" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" }, "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" } }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -1725,11 +2322,22 @@ "node": ">=4" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/exit-hook": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -1737,43 +2345,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extendable-error": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", - "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" } }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -1782,6 +2390,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1793,35 +2402,20 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/find-yarn-workspace-root2": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", - "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", - "dependencies": { - "micromatch": "^4.0.2", - "pkg-dir": "^4.2.0" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" + "node": ">=8" } }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -1832,11 +2426,12 @@ } }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1845,96 +2440,11 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-source": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", - "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", - "dev": true, - "dependencies": { - "data-uri-to-buffer": "^2.0.0", - "source-map": "^0.6.1" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -1946,26 +2456,14 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -1981,270 +2479,67 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, "node_modules/human-id": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/human-id/-/human-id-1.0.2.tgz", - "integrity": "sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.1.tgz", + "integrity": "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==", + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT" }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -2252,91 +2547,20 @@ "node": ">=0.10.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-subdir": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "license": "MIT", "dependencies": { "better-path-resolve": "1.0.0" }, @@ -2344,72 +2568,33 @@ "node": ">=4" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" }, "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -2418,76 +2603,30 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/load-yaml-file": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz", - "integrity": "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==", - "dependencies": { - "graceful-fs": "^4.1.5", - "js-yaml": "^3.13.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/local-pkg": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", - "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "node": ">=6" } }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -2498,181 +2637,95 @@ "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==" + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "license": "MIT" }, "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, "node_modules/magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-6.1.1.tgz", - "integrity": "sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "^4.0.2", - "normalize-package-data": "^2.5.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" - } - }, "node_modules/miniflare": { - "version": "3.20240320.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240320.1.tgz", - "integrity": "sha512-MoHhT+XaFPQtplNIkJc5NtWOi5u/7VkmBUWyyxDH7ehHk4xRT2PDkMCvVOUIcaqbHNIBzigyoYegdYmZcYtdCg==", + "version": "4.20250923.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250923.0.tgz", + "integrity": "sha512-CtO0w3tKr8rl5nS5TchYNGQaXuYLfl1T+IqKQiEoIRAUpVWdiziK49+mKV+Vz6yRENqHEGMYV8EjhfvmEHrJpA==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.2", - "workerd": "1.20240320.1", - "ws": "^8.11.0", - "youch": "^3.2.2", - "zod": "^3.20.6" + "acorn": "8.14.0", + "acorn-walk": "8.3.2", + "exit-hook": "2.2.1", + "glob-to-regexp": "0.4.1", + "sharp": "^0.33.5", + "stoppable": "1.1.0", + "undici": "7.14.0", + "workerd": "1.20250923.0", + "ws": "8.18.0", + "youch": "4.1.0-beta.10", + "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" }, "engines": { - "node": ">=16.13" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" + "node": ">=18.0.0" } }, - "node_modules/mixme": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.5.9.tgz", - "integrity": "sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw==", + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/mlly": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz", - "integrity": "sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "pathe": "^1.1.1", - "pkg-types": "^1.0.3", - "ufo": "^1.1.2" + "node": ">=4" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "bin": { - "mustache": "bin/mustache" - } + "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -2680,6 +2733,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2688,9 +2742,10 @@ } }, "node_modules/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2706,75 +2761,17 @@ } } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/outdent": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", - "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==" + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "license": "MIT" }, "node_modules/p-filter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "license": "MIT", "dependencies": { "p-map": "^2.0.0" }, @@ -2786,6 +2783,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -2800,6 +2798,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -2811,6 +2810,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2819,73 +2819,75 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "quansync": "^0.2.7" } }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pathe": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", - "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2897,36 +2899,15 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" - } - }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -2942,101 +2923,21 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/preferred-pm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz", - "integrity": "sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==", - "dependencies": { - "find-up": "^5.0.0", - "find-yarn-workspace-root2": "1.2.16", - "path-exists": "^4.0.0", - "which-pm": "2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/preferred-pm/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", "bin": { "prettier": "bin-prettier.js" }, @@ -3047,42 +2948,21 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/printable-characters": { - "version": "1.0.42", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", - "dev": true - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -3098,177 +2978,85 @@ "url": "https://www.patreon.com/feross" }, { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/read-yaml-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", - "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", - "dependencies": { - "graceful-fs": "^4.1.5", - "js-yaml": "^3.6.1", - "pify": "^4.0.1", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "node_modules/read-yaml-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "graceful-fs": "^4.1.5", + "js-yaml": "^3.6.1", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rollup": { - "version": "3.27.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.2.tgz", - "integrity": "sha512-YGwmHf7h2oUHkVBT248x0yt6vZkYQ3/rvE5iQuVBh3WO8GcJ6BNeOkpoX1yMHIiBm18EMLjBPIoUDkhgnyxGOQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", + "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.2", + "@rollup/rollup-android-arm64": "4.52.2", + "@rollup/rollup-darwin-arm64": "4.52.2", + "@rollup/rollup-darwin-x64": "4.52.2", + "@rollup/rollup-freebsd-arm64": "4.52.2", + "@rollup/rollup-freebsd-x64": "4.52.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", + "@rollup/rollup-linux-arm-musleabihf": "4.52.2", + "@rollup/rollup-linux-arm64-gnu": "4.52.2", + "@rollup/rollup-linux-arm64-musl": "4.52.2", + "@rollup/rollup-linux-loong64-gnu": "4.52.2", + "@rollup/rollup-linux-ppc64-gnu": "4.52.2", + "@rollup/rollup-linux-riscv64-gnu": "4.52.2", + "@rollup/rollup-linux-riscv64-musl": "4.52.2", + "@rollup/rollup-linux-s390x-gnu": "4.52.2", + "@rollup/rollup-linux-x64-gnu": "4.52.2", + "@rollup/rollup-linux-x64-musl": "4.52.2", + "@rollup/rollup-openharmony-arm64": "4.52.2", + "@rollup/rollup-win32-arm64-msvc": "4.52.2", + "@rollup/rollup-win32-ia32-msvc": "4.52.2", + "@rollup/rollup-win32-x64-gnu": "4.52.2", + "@rollup/rollup-win32-x64-msvc": "4.52.2", "fsevents": "~2.3.2" } }, @@ -3290,52 +3078,22 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3343,625 +3101,339 @@ "node": ">=10" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } }, "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/smartwrap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/smartwrap/-/smartwrap-2.0.2.tgz", - "integrity": "sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==", - "dependencies": { - "array.prototype.flat": "^1.2.3", - "breakword": "^1.0.5", - "grapheme-splitter": "^1.0.4", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1", - "yargs": "^15.1.0" - }, - "bin": { - "smartwrap": "src/terminal-adapter.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/smartwrap/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/smartwrap/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/smartwrap/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/smartwrap/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/smartwrap/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "is-arrayish": "^0.3.1" } }, - "node_modules/smartwrap/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/smartwrap/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/spawndamnit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-2.0.0.tgz", - "integrity": "sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==", - "dependencies": { - "cross-spawn": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "node_modules/spdx-expression-parse": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", + "license": "SEE LICENSE IN LICENSE", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/stacktracey": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", - "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", "dev": true, - "dependencies": { - "as-table": "^1.0.36", - "get-source": "^2.0.12" - } + "license": "MIT" }, "node_modules/std-env": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", - "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", - "dev": true + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" }, "node_modules/stoppable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4", "npm": ">=6" } }, - "node_modules/stream-transform": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.3.tgz", - "integrity": "sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==", - "dependencies": { - "mixme": "^0.5.1" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", - "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", - "dev": true, - "dependencies": { - "acorn": "^8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tinybench": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", - "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", - "dev": true - }, - "node_modules/tinypool": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz", - "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", - "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + }, "engines": { "node": ">=8" } }, - "node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", - "dev": true - }, - "node_modules/tty-table": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/tty-table/-/tty-table-4.2.1.tgz", - "integrity": "sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==", - "dependencies": { - "chalk": "^4.1.2", - "csv": "^5.5.3", - "kleur": "^4.1.5", - "smartwrap": "^2.0.2", - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.1", - "yargs": "^17.7.1" - }, - "bin": { - "tty-table": "adapters/terminal-adapter.js" - }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">=4" } }, - "node_modules/tty-table/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "js-tokens": "^9.0.1" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/tty-table/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/tty-table/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "license": "MIT", "engines": { - "node": ">=7.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tty-table/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" }, - "node_modules/tty-table/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" }, - "node_modules/tty-table/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^18.0.0 || >=20.0.0" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.0.0" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.0.0" } }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "is-number": "^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3970,99 +3442,92 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.2.0.tgz", - "integrity": "sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==", - "dev": true - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz", + "integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==", "dev": true, - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=14.0" + "node": ">=20.18.1" } }, + "node_modules/undici-types": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", + "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/vite": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", - "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", - "less": "*", + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -4072,6 +3537,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -4080,197 +3548,192 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, "node_modules/vite-node": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.32.4.tgz", - "integrity": "sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, + "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.4", - "mlly": "^1.4.0", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^3.0.0 || ^4.0.0" + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" }, "engines": { - "node": ">=v14.18.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/vitest": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", - "integrity": "sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==", + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "dependencies": { - "@types/chai": "^4.3.5", - "@types/chai-subset": "^1.3.3", - "@types/node": "*", - "@vitest/expect": "0.32.4", - "@vitest/runner": "0.32.4", - "@vitest/snapshot": "0.32.4", - "@vitest/spy": "0.32.4", - "@vitest/utils": "0.32.4", - "acorn": "^8.9.0", - "acorn-walk": "^8.2.0", - "cac": "^6.7.14", - "chai": "^4.3.7", - "debug": "^4.3.4", - "local-pkg": "^0.4.3", - "magic-string": "^0.30.0", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.3.3", - "strip-literal": "^1.0.1", - "tinybench": "^2.5.0", - "tinypool": "^0.5.0", - "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.32.4", - "why-is-node-running": "^2.2.2" + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": ">=v14.18.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@vitest/browser": "*", - "@vitest/ui": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", - "jsdom": "*", - "playwright": "*", - "safaridriver": "*", - "webdriverio": "*" + "jsdom": "*" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { + "@types/debug": { "optional": true }, - "happy-dom": { + "@types/node": { "optional": true }, - "jsdom": { + "@vitest/browser": { "optional": true }, - "playwright": { + "@vitest/ui": { "optional": true }, - "safaridriver": { + "happy-dom": { "optional": true }, - "webdriverio": { + "jsdom": { "optional": true } } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" - }, - "node_modules/which-pm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.0.0.tgz", - "integrity": "sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==", - "dependencies": { - "load-yaml-file": "^0.2.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8.15" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "node-which": "bin/node-which" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, + "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" @@ -4283,11 +3746,12 @@ } }, "node_modules/workerd": { - "version": "1.20240320.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240320.1.tgz", - "integrity": "sha512-nuavAGGjh0qqM6RF5zxTHyUwEqdLCHchodbrpbh/xlJpFGnJVY5C1YgSi2S9aLkJJoa0/25Ta/+EzXEbApA/3w==", + "version": "1.20250923.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250923.0.tgz", + "integrity": "sha512-avGZgJe3Vug0ff8oq5Hpa//x0dF9b12jKhDKqaEZaWl7mVGQk+GaA9lrO8TyJxzlfPIr/rXdvcRYJi/hbdgIJw==", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", "bin": { "workerd": "bin/workerd" }, @@ -4295,64 +3759,19 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20240320.1", - "@cloudflare/workerd-darwin-arm64": "1.20240320.1", - "@cloudflare/workerd-linux-64": "1.20240320.1", - "@cloudflare/workerd-linux-arm64": "1.20240320.1", - "@cloudflare/workerd-windows-64": "1.20240320.1" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@cloudflare/workerd-darwin-64": "1.20250923.0", + "@cloudflare/workerd-darwin-arm64": "1.20250923.0", + "@cloudflare/workerd-linux-64": "1.20250923.0", + "@cloudflare/workerd-linux-arm64": "1.20250923.0", + "@cloudflare/workerd-windows-64": "1.20250923.0" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -4369,84 +3788,37 @@ } } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "node_modules/youch": { + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" } }, - "node_modules/youch": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/youch/-/youch-3.2.3.tgz", - "integrity": "sha512-ZBcWz/uzZaQVdCvfV4uk616Bbpf2ee+F/AvuKDR5EwX/Y4v06xWdtMluqTD7+KlZdM93lLm9gMZYo0sKBS0pgw==", + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", "dev": true, + "license": "MIT", "dependencies": { - "cookie": "^0.5.0", - "mustache": "^4.2.0", - "stacktracey": "^2.1.8" + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" } }, "node_modules/zod": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", - "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", + "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 05e1753e..7f3130ea 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "dependencies": { "@changesets/changelog-github": "^0.4.8", - "@changesets/cli": "^2.26.2", - "@cloudflare/workers-types": "^4.20230807.0" + "@changesets/cli": "^2.29.6", + "@cloudflare/workers-types": "^4.20250826.0" }, "name": "workers-rs", "version": "0.0.12", @@ -14,16 +14,19 @@ }, "homepage": "https://github.com/cloudflare/workers-rs#readme", "devDependencies": { - "@types/node": "^20.4.8", - "@types/uuid": "^9.0.2", - "miniflare": "^3.20231030.2", - "typescript": "^5.1.6", - "undici": "^5.28.4", - "uuid": "^9.0.0", - "vitest": "^0.32.4" + "@types/node": "^24.0.1", + "miniflare": "^4.20250923.0", + "typescript": "^5.8.3", + "undici": "7.14.0", + "uuid": "^11.1.0", + "vitest": "^3.2.4" }, "scripts": { - "test": "cd worker-sandbox && NO_MINIFY=1 worker-build --dev && NODE_OPTIONS=--experimental-vm-modules npx vitest run", - "test-http": "cd worker-sandbox && NO_MINIFY=1 worker-build --dev --features http && NODE_OPTIONS=--experimental-vm-modules npx vitest run" + "build": "cd wasm-bindgen && cargo build -p wasm-bindgen-cli --bin wasm-bindgen && cd .. && cargo build -p worker-build", + "test": "cd test && NO_MINIFY=1 WASM_BINDGEN_PATH=../wasm-bindgen/target/debug/wasm-bindgen ../target/debug/worker-build --dev && NODE_OPTIONS='--experimental-vm-modules' npx vitest run", + "test-http": "cd test && NO_MINIFY=1 WASM_BINDGEN_PATH=../wasm-bindgen/target/debug/wasm-bindgen ../target/debug/worker-build --release --features http && NODE_OPTIONS='--experimental-vm-modules' npx vitest run", + "test-mem": "cd test && npx wrangler dev --enable-containers=false", + "lint": "cargo clippy --features d1,queue --all-targets --workspace -- -D warnings", + "lint:fix": "cargo fmt && cargo clippy --features d1,queue --all-targets --workspace --fix -- -D warnings" } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..bc68cdfe --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.82.0" \ No newline at end of file diff --git a/templates/axum/Cargo.toml b/templates/axum/Cargo.toml index 9b1c2c71..5646f7a1 100644 --- a/templates/axum/Cargo.toml +++ b/templates/axum/Cargo.toml @@ -2,21 +2,13 @@ name = "{{project-name}}" version = "0.1.0" edition = "2021" -authors = [ "{{authors}}" ] - -[package.metadata.release] -release = false - -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false +authors = ["{{authors}}"] [lib] crate-type = ["cdylib"] [dependencies] -worker = { version="0.2.0", features=['http', 'axum'] } -worker-macros = { version="0.2.0", features=['http'] } -axum = { version = "0.7", default-features = false } -tower-service = "0.3.2" -console_error_panic_hook = { version = "0.1.1" } \ No newline at end of file +worker = { version = "0.6", features = ['http', 'axum'] } +worker-macros = { version = "0.6", features = ['http'] } +axum = { version = "0.8", default-features = false } +tower-service = "0.3.3" diff --git a/templates/axum/src/lib.rs b/templates/axum/src/lib.rs index 08a2fc2e..f80ee2b9 100644 --- a/templates/axum/src/lib.rs +++ b/templates/axum/src/lib.rs @@ -12,7 +12,6 @@ async fn fetch( _env: Env, _ctx: Context, ) -> Result> { - console_error_panic_hook::set_once(); Ok(router().call(req).await?) } diff --git a/templates/axum/wrangler.toml b/templates/axum/wrangler.toml index c85247f7..e383bdff 100644 --- a/templates/axum/wrangler.toml +++ b/templates/axum/wrangler.toml @@ -1,5 +1,5 @@ name = "{{project-name}}" -main = "build/worker/shim.mjs" +main = "build/index.js" compatibility_date = "{{ "now" | date: "%Y-%m-%d" }}" [build] diff --git a/templates/hello-world-http/Cargo.toml b/templates/hello-world-http/Cargo.toml index 8d94accc..69109fa5 100644 --- a/templates/hello-world-http/Cargo.toml +++ b/templates/hello-world-http/Cargo.toml @@ -2,20 +2,12 @@ name = "{{project-name}}" version = "0.1.0" edition = "2021" -authors = [ "{{authors}}" ] - -[package.metadata.release] -release = false - -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false +authors = ["{{authors}}"] [lib] crate-type = ["cdylib"] [dependencies] -worker = { version="0.2.0", features=['http'] } -worker-macros = { version="0.2.0", features=['http'] } -console_error_panic_hook = { version = "0.1.1" } -http = "1.1" \ No newline at end of file +worker = { version = "0.6", features = ['http'] } +worker-macros = { version = "0.6", features = ['http'] } +http = "1.3" diff --git a/templates/hello-world-http/src/lib.rs b/templates/hello-world-http/src/lib.rs index 1722297b..15144b8f 100644 --- a/templates/hello-world-http/src/lib.rs +++ b/templates/hello-world-http/src/lib.rs @@ -6,7 +6,6 @@ async fn fetch( _env: Env, _ctx: Context, ) -> Result { - console_error_panic_hook::set_once(); Ok(http::Response::builder() .status(http::StatusCode::OK) .body(Body::empty())?) diff --git a/templates/hello-world-http/wrangler.toml b/templates/hello-world-http/wrangler.toml index c85247f7..e383bdff 100644 --- a/templates/hello-world-http/wrangler.toml +++ b/templates/hello-world-http/wrangler.toml @@ -1,5 +1,5 @@ name = "{{project-name}}" -main = "build/worker/shim.mjs" +main = "build/index.js" compatibility_date = "{{ "now" | date: "%Y-%m-%d" }}" [build] diff --git a/templates/hello-world/Cargo.toml b/templates/hello-world/Cargo.toml index 569a8265..0d035948 100644 --- a/templates/hello-world/Cargo.toml +++ b/templates/hello-world/Cargo.toml @@ -2,19 +2,11 @@ name = "{{project-name}}" version = "0.1.0" edition = "2021" -authors = [ "{{authors}}" ] - -[package.metadata.release] -release = false - -# https://github.com/rustwasm/wasm-pack/issues/1247 -[package.metadata.wasm-pack.profile.release] -wasm-opt = false +authors = ["{{authors}}"] [lib] crate-type = ["cdylib"] [dependencies] -worker = { version="0.2.0" } -worker-macros = { version="0.2.0" } -console_error_panic_hook = { version = "0.1.1" } \ No newline at end of file +worker = { version = "0.6" } +worker-macros = { version = "0.6" } diff --git a/templates/hello-world/src/lib.rs b/templates/hello-world/src/lib.rs index e26ec0e1..a7daced2 100644 --- a/templates/hello-world/src/lib.rs +++ b/templates/hello-world/src/lib.rs @@ -6,6 +6,5 @@ async fn fetch( _env: Env, _ctx: Context, ) -> Result { - console_error_panic_hook::set_once(); Response::ok("Hello World!") } \ No newline at end of file diff --git a/templates/hello-world/wrangler.toml b/templates/hello-world/wrangler.toml index c85247f7..e383bdff 100644 --- a/templates/hello-world/wrangler.toml +++ b/templates/hello-world/wrangler.toml @@ -1,5 +1,5 @@ name = "{{project-name}}" -main = "build/worker/shim.mjs" +main = "build/index.js" compatibility_date = "{{ "now" | date: "%Y-%m-%d" }}" [build] diff --git a/templates/leptos/.cargo/config.toml b/templates/leptos/.cargo/config.toml new file mode 100644 index 00000000..0e465b2d --- /dev/null +++ b/templates/leptos/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] diff --git a/templates/leptos/.gitignore b/templates/leptos/.gitignore new file mode 100644 index 00000000..96a69c08 --- /dev/null +++ b/templates/leptos/.gitignore @@ -0,0 +1,4 @@ +target +node_modules +.wrangler +build \ No newline at end of file diff --git a/templates/leptos/Cargo.toml b/templates/leptos/Cargo.toml new file mode 100644 index 00000000..c82af767 --- /dev/null +++ b/templates/leptos/Cargo.toml @@ -0,0 +1,97 @@ +[package] +name = "{{project-name}}" +version = "0.1.0" +edition = "2021" +authors = ["{{authors}}"] + +[profile.release] +opt-level = "s" # optimize for size in release builds +lto = true +strip = true +codegen-units = 1 + +[lib] +crate-type = ["cdylib"] + +[dependencies] +axum = { version = "0.8", default-features = false, optional = true } +getrandom = { version = "0.3", features = ["wasm_js"]} +leptos = { version = "0.8"{% if use_nightly %}, features = ["nightly"]{% endif %} } +leptos_axum = { version = "0.8", default-features = false, features = ["wasm"], optional = true } +leptos_meta = { version = "0.8" } +leptos_router = { version = "0.8"{% if use_nightly %}, features = ["nightly"]{% endif %} } +tower-service = "0.3" +wasm-bindgen = "0.2" +worker = { version = "0.6", features = ["http", "axum", "d1"], optional = true } + +[features] +hydrate = ["leptos/hydrate"] +ssr = [ + "dep:axum", + "dep:leptos_axum", + "dep:worker", + "leptos/ssr", + "leptos_router/ssr", +] + +[package.metadata.leptos] +wasm-validation = false +# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name +output-name = "{{project-name}}" + +# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup. +site-root = "target/site" + +# The site-root relative folder where all compiled output (JS, WASM and CSS) is written +# Defaults to pkg +site-pkg-dir = "pkg" + +# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to //app.css +style-file = "style/main.css" + +# Assets source dir. All files found here will be copied and synchronized to site-root. +# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir. +# +# Optional. Env: LEPTOS_ASSETS_DIR. +assets-dir = "assets" + +# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup. +site-addr = "127.0.0.1:8787" + +# The port to use for automatic reload monitoring +reload-port = 3001 + +# [Optional] Command to use when running end2end tests. It will run in the end2end dir. +# [Windows] for non-WSL use "npx.cmd playwright test" +# This binary name can be checked in Powershell with Get-Command npx +end2end-cmd = "cargo make test-ui" +end2end-dir = "end2end" + +# The browserlist query used for optimizing the CSS. +browserquery = "defaults" + +# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head +watch = false + +# The environment Leptos will run in, usually either "DEV" or "PROD" +env = "DEV" + +# The features to use when compiling the bin target +# +# Optional. Can be over-ridden with the command line parameter --bin-features +bin-features = ["ssr"] + +# If the --no-default-features flag should be used when compiling the bin target +# +# Optional. Defaults to false. +bin-default-features = false + +# The features to use when compiling the lib target +# +# Optional. Can be over-ridden with the command line parameter --lib-features +lib-features = ["hydrate"] + +# If the --no-default-features flag should be used when compiling the lib target +# +# Optional. Defaults to false. +lib-default-features = false \ No newline at end of file diff --git a/templates/leptos/README.md b/templates/leptos/README.md new file mode 100644 index 00000000..e12ad66c --- /dev/null +++ b/templates/leptos/README.md @@ -0,0 +1,38 @@ +# Leptos + +This template demonstrates the use of the [Leptos](https://leptos.dev/) +framework on Workers, including support for server side rendering and +server functions. + +Frontend assets are built using `cargo leptos` by compiling the crate +with the `hydrate` feature. The backend module uses `workers-rs` and +is built by compiling the crate using `worker-build` with the `ssr` +feature. This is done automatically when using `wrangler` with +the custom build command specified in `wrangler.toml`. + +Frontend assets are served using Workers Assets. Any request which +matches an asset path will be served directly and not invoke the +Worker. Requests which do not match an asset path will invoke the +Worker. This includes requests to `index.html` (which will be +server-side rendered) and any server function (API) routes. + +# Setup + +[Cargo Leptos](https://github.com/leptos-rs/cargo-leptos) is required +to build the project. + +``` +cargo install --locked cargo-leptos +``` + +# Run Locally + +``` +npx wrangler dev +``` + +# Deploy + +``` +npx wrangler deploy +``` diff --git a/templates/leptos/assets/favicon.ico b/templates/leptos/assets/favicon.ico new file mode 100644 index 00000000..d26f1fc8 Binary files /dev/null and b/templates/leptos/assets/favicon.ico differ diff --git a/templates/leptos/cargo-generate.toml b/templates/leptos/cargo-generate.toml new file mode 100644 index 00000000..9c7a0b8c --- /dev/null +++ b/templates/leptos/cargo-generate.toml @@ -0,0 +1,5 @@ +[placeholders] +use_nightly = { prompt = "Use nightly features?", default = false, type = "bool" } + +[template] +exclude = ["*.ico"] diff --git a/templates/leptos/src/app.rs b/templates/leptos/src/app.rs new file mode 100644 index 00000000..9440c397 --- /dev/null +++ b/templates/leptos/src/app.rs @@ -0,0 +1,56 @@ +use leptos::prelude::*; +use leptos_meta::{provide_meta_context, MetaTags, Stylesheet}; +use leptos_router::{ + components::{Route, Router, Routes}, + StaticSegment, +}; + +use crate::components::show_data_from_api::ShowDataFromApi; + +pub fn shell(options: LeptosOptions) -> impl IntoView { + view! { + + + + + + + + + + + + + + } +} + +#[component] +pub fn App() -> impl IntoView { + // Provides context that manages stylesheets, titles, meta tags, etc. + provide_meta_context(); + + view! { + // injects a stylesheet into the document + // id=leptos means cargo-leptos will hot-reload this stylesheet + + + // content for this welcome page + +
+ + + +
+
+ } +} + +/// Renders the home page of your application. +#[component] +fn HomePage() -> impl IntoView { + view! { +

"Hello world!"

+ + } +} \ No newline at end of file diff --git a/templates/leptos/src/components.rs b/templates/leptos/src/components.rs new file mode 100644 index 00000000..235c9b10 --- /dev/null +++ b/templates/leptos/src/components.rs @@ -0,0 +1 @@ +pub mod show_data_from_api; \ No newline at end of file diff --git a/templates/leptos/src/components/show_data_from_api.rs b/templates/leptos/src/components/show_data_from_api.rs new file mode 100644 index 00000000..5ef8f8d7 --- /dev/null +++ b/templates/leptos/src/components/show_data_from_api.rs @@ -0,0 +1,27 @@ +use leptos::prelude::*; + +#[server(SayHello)] +pub async fn say_hello(num: i32) -> Result { + Ok(format!("Hello from the API!!! I got {num}")) +} + +#[component] +pub fn ShowDataFromApi() -> impl IntoView { + let value = RwSignal::new("".to_string()); + let counter = RwSignal::new(0); + + let on_click = move |_| { + leptos::task::spawn_local(async move { + let api_said = say_hello(counter.get()).await.unwrap(); + value.set(api_said); + counter.update(|v| *v += 1); + }); + }; + + view! { +
+ +

{value}

+
+ } +} \ No newline at end of file diff --git a/templates/leptos/src/lib.rs b/templates/leptos/src/lib.rs new file mode 100644 index 00000000..1e692d8f --- /dev/null +++ b/templates/leptos/src/lib.rs @@ -0,0 +1,56 @@ +#[cfg(feature = "ssr")] +use worker::*; + +use crate::app::*; + +pub mod app; +mod components; + +#[cfg(feature = "ssr")] +pub fn register_server_functions() { + use leptos::server_fn::axum::register_explicit; + + // Add all of your server functions here + register_explicit::(); +} + +#[cfg(feature = "ssr")] +async fn router(env: Env) -> axum::Router { + use std::sync::Arc; + + use axum::{Extension, Router}; + use leptos::prelude::*; + use leptos_axum::{generate_route_list, LeptosRoutes}; + + let conf = get_configuration(None).unwrap(); + let leptos_options = conf.leptos_options; + let routes = generate_route_list(App); + register_server_functions(); + + // build our application with a route + Router::new() + .leptos_routes(&leptos_options, routes, { + let leptos_options = leptos_options.clone(); + move || shell(leptos_options.clone()) + }) + .with_state(leptos_options) + .layer(Extension(Arc::new(env))) // <- Allow leptos server functions to access Worker stuff +} + +#[cfg(feature = "ssr")] +#[event(fetch)] +async fn fetch( + req: HttpRequest, + env: Env, + _ctx: Context, +) -> Result> { + use tower_service::Service; + + Ok(router(env).await.call(req).await?) +} + +#[cfg(feature = "hydrate")] +#[wasm_bindgen::prelude::wasm_bindgen] +pub fn hydrate() { + leptos::mount::hydrate_body(App); +} diff --git a/templates/leptos/src/main.rs b/templates/leptos/src/main.rs new file mode 100644 index 00000000..583e68cc --- /dev/null +++ b/templates/leptos/src/main.rs @@ -0,0 +1,5 @@ +pub fn main() { + // no client-side main function + // unless we want this to work with e.g., Trunk for pure client-side testing + // see lib.rs for hydration function instead +} \ No newline at end of file diff --git a/templates/leptos/src/mod.rs b/templates/leptos/src/mod.rs new file mode 100644 index 00000000..23caeca2 --- /dev/null +++ b/templates/leptos/src/mod.rs @@ -0,0 +1 @@ +pub mod show_data_from_api; diff --git a/templates/leptos/src/say_hello.rs b/templates/leptos/src/say_hello.rs new file mode 100644 index 00000000..37f5bac3 --- /dev/null +++ b/templates/leptos/src/say_hello.rs @@ -0,0 +1,6 @@ +use leptos::prelude::*; + +#[server(SayHello)] +pub async fn say_hello(num: i32) -> Result { + Ok(format!("Hello from the API!!! I got {num}")) +} diff --git a/templates/leptos/src/show_data_from_api.rs b/templates/leptos/src/show_data_from_api.rs new file mode 100644 index 00000000..db28e4e9 --- /dev/null +++ b/templates/leptos/src/show_data_from_api.rs @@ -0,0 +1,25 @@ +use leptos::prelude::*; +use wasm_bindgen_futures::spawn_local; + +use crate::api::say_hello::say_hello; + +#[component] +pub fn ShowDataFromApi() -> impl IntoView { + let value = create_rw_signal("".to_string()); + let counter = create_rw_signal(0); + + let on_click = move |_| { + spawn_local(async move { + let api_said = say_hello(counter.get()).await.unwrap(); + value.set(api_said); + counter.update(|v| *v += 1); + }); + }; + + view! { +
+ +

{value}

+
+ } +} diff --git a/templates/leptos/style/main.css b/templates/leptos/style/main.css new file mode 100644 index 00000000..7ff938e6 --- /dev/null +++ b/templates/leptos/style/main.css @@ -0,0 +1 @@ +/* Add your CSS code here */ \ No newline at end of file diff --git a/templates/leptos/wrangler.toml b/templates/leptos/wrangler.toml new file mode 100644 index 00000000..4536049d --- /dev/null +++ b/templates/leptos/wrangler.toml @@ -0,0 +1,9 @@ +name = "{{project-name}}" +main = "build/index.js" +compatibility_date = "{{ "now" | date: "%Y-%m-%d" }}" + +[build] +command = "cargo leptos build --release && cargo install -q worker-build && LEPTOS_OUTPUT_NAME={{project-name}} worker-build --release --features ssr" + +[assets] +directory = "./target/site" \ No newline at end of file diff --git a/test/.dev.vars b/test/.dev.vars new file mode 100644 index 00000000..87c2fea3 --- /dev/null +++ b/test/.dev.vars @@ -0,0 +1 @@ +SOME_SECRET="super secret" diff --git a/worker-sandbox/.gitignore b/test/.gitignore similarity index 100% rename from worker-sandbox/.gitignore rename to test/.gitignore diff --git a/worker-sandbox/CODE_OF_CONDUCT.md b/test/CODE_OF_CONDUCT.md similarity index 100% rename from worker-sandbox/CODE_OF_CONDUCT.md rename to test/CODE_OF_CONDUCT.md diff --git a/test/Cargo.toml b/test/Cargo.toml new file mode 100644 index 00000000..25620ebb --- /dev/null +++ b/test/Cargo.toml @@ -0,0 +1,70 @@ +[package] +description = "A functioning Cloudflare Worker for testing features and ergonomics" +authors = ["Cloudflare Workers Team "] +edition = "2018" +name = "worker-sandbox" +version = "0.1.0" +license = "Apache-2.0" + +[package.metadata.release] +release = false + +# Change to `dwarf-debug-info = true` to preserve debug symbols in the dev +# profile. We leave this as false (the default) to avoid breaking tests, +# although tests _do_ seem to work when we update package.json to run vitest +# with `--test-timeout=0 --no-file-parallelism`. +# https://developers.cloudflare.com/workers/observability/dev-tools/cpu-usage/#taking-a-profile +# [package.metadata.wasm-pack.profile.dev.wasm-bindgen] +# dwarf-debug-info = false + +[lib] +crate-type = ["cdylib", "rlib"] +path = "src/lib.rs" + +[features] +http = [ + "worker/http", + "worker/axum", + "dep:axum", + "dep:tower-service", + "dep:axum-macros", +] + +[dependencies] +futures-channel.workspace = true +futures-util.workspace = true +blake2 = "0.10" +chrono = { version = "0.4", default-features = false, features = [ + "wasmbind", + "clock", +] } +cfg-if = "1.0" +getrandom = { version = "0.3", features = ["wasm_js"] } +gloo-timers = { version = "0.3.0", features = ["futures"] } +hex = "0.4" +http.workspace = true +regex = "1.8.4" +serde.workspace = true +serde_json.workspace = true +worker.workspace = true +rand = "0.9.1" +uuid = { version = "1.17", features = ["v4", "serde", "js"] } +serde-wasm-bindgen.workspace = true +wasm-bindgen.workspace = true +md5 = "0.7.0" +tokio-stream = "0.1" +tokio = { version = "1.45", default-features = false, features = ['io-util'] } +axum = { version = "0.8", optional = true, default-features = false } +axum-macros = { version = "0.5", optional = true, default-features = false } +tower-service = { version = "0.3", optional = true } +paste = "1.0.15" + +[dev-dependencies] +wasm-bindgen-test.workspace = true +futures-channel = { version = "0.3", features = ["sink"] } +futures-util = { version = "0.3", default-features = false, features = [ + "sink", +] } +tokio = { version = "1.45", features = ["macros", "rt", "test-util"] } +tungstenite = "0.27" +retry = "2.1" diff --git a/worker-sandbox/LICENSE b/test/LICENSE similarity index 100% rename from worker-sandbox/LICENSE rename to test/LICENSE diff --git a/worker-sandbox/README.md b/test/README.md similarity index 100% rename from worker-sandbox/README.md rename to test/README.md diff --git a/test/container-echo/Cargo.toml b/test/container-echo/Cargo.toml new file mode 100644 index 00000000..ef99bb37 --- /dev/null +++ b/test/container-echo/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "container-echo" +version = "0.1.5" +edition = "2021" + +[dependencies] +axum = { version = "0.8.4", features = ["ws"] } +tokio = { version = "1.47.1", features = ["rt-multi-thread", "net"] } diff --git a/test/container-echo/Dockerfile b/test/container-echo/Dockerfile new file mode 100644 index 00000000..ed0d2921 --- /dev/null +++ b/test/container-echo/Dockerfile @@ -0,0 +1,10 @@ +FROM rust:alpine AS builder +COPY ./ /app +WORKDIR /app +RUN apk add musl-dev +RUN cargo build --release + +FROM alpine:latest +COPY --from=builder /app/target/release/container-echo /container-echo +ENTRYPOINT ["/container-echo"] +EXPOSE 8080 diff --git a/test/container-echo/README.md b/test/container-echo/README.md new file mode 100644 index 00000000..40c0f9bc --- /dev/null +++ b/test/container-echo/README.md @@ -0,0 +1,2 @@ +# Echo Server +A simple echo server. diff --git a/test/container-echo/src/main.rs b/test/container-echo/src/main.rs new file mode 100644 index 00000000..9d9461ae --- /dev/null +++ b/test/container-echo/src/main.rs @@ -0,0 +1,37 @@ +use std::io; + +use axum::{ + body::Body, + extract::ws::{WebSocket, WebSocketUpgrade}, + response::Response, + routing::{get, post}, + Router, +}; +use tokio::net::TcpListener; + +#[tokio::main] +async fn main() -> io::Result<()> { + let router = Router::new() + .route("/ping", get(|| async {})) + .route("/echo", post(|b: Body| async { b })) + .route("/ws", get(ws_handler)); + let listener = TcpListener::bind("0.0.0.0:8080").await?; + axum::serve(listener, router).await?; + Ok(()) +} + +async fn ws_handler(ws: WebSocketUpgrade) -> Response { + ws.on_upgrade(handle_ws) +} + +async fn handle_ws(mut ws: WebSocket) { + while let Some(msg) = ws.recv().await { + let msg = match msg { + Ok(v) => v, + Err(_) => return, + }; + if ws.send(msg).await.is_err() { + return; + } + } +} diff --git a/test/public/test.txt b/test/public/test.txt new file mode 100644 index 00000000..3b124649 --- /dev/null +++ b/test/public/test.txt @@ -0,0 +1 @@ +TEST \ No newline at end of file diff --git a/test/src/alarm.rs b/test/src/alarm.rs new file mode 100644 index 00000000..fff5a3a1 --- /dev/null +++ b/test/src/alarm.rs @@ -0,0 +1,49 @@ +use std::time::Duration; +use worker::{console_log, durable_object, wasm_bindgen, Env, Request, Response, Result, State}; + +use super::SomeSharedData; + +#[durable_object] +pub struct AlarmObject { + state: State, + // used for memory leak detection + _buffer: Vec, +} + +impl DurableObject for AlarmObject { + fn new(state: State, _: Env) -> Self { + Self { + state, + _buffer: Vec::with_capacity(111_000_000), + } + } + + async fn fetch(&self, _: Request) -> Result { + self.state + .storage() + .set_alarm(Duration::from_millis(100)) + .await?; + let alarmed: bool = match self.state.storage().get("alarmed").await { + Ok(alarmed) => alarmed, + Err(e) if e.to_string() == "No such value in storage." => false, + Err(e) => return Err(e), + }; + Response::ok(alarmed.to_string()) + } + + async fn alarm(&self) -> Result { + self.state.storage().put("alarmed", true).await?; + console_log!("Alarm has been triggered!"); + Response::ok("ALARMED") + } +} + +#[worker::send] +pub async fn handle_alarm(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let namespace = env.durable_object("ALARM")?; + let stub = namespace.id_from_name("alarm")?.get_stub()?; + // when calling fetch to a Durable Object, a full URL must be used. Alternatively, a + // compatibility flag can be provided in wrangler.toml to opt-in to older behavior: + // https://developers.cloudflare.com/workers/platform/compatibility-dates#durable-object-stubfetch-requires-a-full-url + stub.fetch_with_str("https://fake-host/alarm").await +} diff --git a/test/src/analytics_engine.rs b/test/src/analytics_engine.rs new file mode 100644 index 00000000..cd0880d9 --- /dev/null +++ b/test/src/analytics_engine.rs @@ -0,0 +1,35 @@ +use super::SomeSharedData; +use uuid::Uuid; +use worker::{AnalyticsEngineDataPointBuilder, Env, Request, Response, Result}; + +#[worker::send] +pub async fn handle_analytics_event( + req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let dataset = match env.analytics_engine("HTTP_ANALYTICS") { + Ok(dataset) => dataset, + Err(err) => return Response::error(format!("Failed to get dataset: {err:?}"), 500), + }; + + let request_id = Uuid::new_v4(); + // Build the event and write it to analytics engine + let point = AnalyticsEngineDataPointBuilder::new() + .indexes(vec!["index1"]) + .add_blob(req.method().as_ref()) // blob1 + .add_blob(request_id.as_bytes().as_ref()) // blob2 + .add_double(200) + .build(); + dataset.write_data_point(&point)?; + + // Or write it directly from the builder using write_to + AnalyticsEngineDataPointBuilder::new() + .indexes(["index1"]) + .add_blob(req.method().as_ref()) // blob1 + .add_blob(req.method().as_ref()) // blob2 + .add_double(200) + .write_to(&dataset)?; + + return Response::ok("Events sent"); +} diff --git a/test/src/assets.rs b/test/src/assets.rs new file mode 100644 index 00000000..f3bedc0f --- /dev/null +++ b/test/src/assets.rs @@ -0,0 +1,37 @@ +use worker::Url; + +#[cfg(not(feature = "http"))] +pub async fn handle_asset( + req: worker::Request, + env: worker::Env, + _data: crate::SomeSharedData, +) -> worker::Result { + let url: Url = req.url()?; + let name: String = url.path_segments().unwrap().nth(1).unwrap().to_string(); + let url: String = ["https://dummyurl.com/", &name].concat(); + + env.assets("ASSETS") + .expect("ASSETS BINDING") + .fetch(url, None) + .await +} + +#[cfg(feature = "http")] +#[worker::send] +pub async fn handle_asset( + req: worker::Request, + env: worker::Env, + _data: crate::SomeSharedData, +) -> worker::Result { + use std::convert::TryInto; + + let url: Url = req.url()?; + let name: String = url.path_segments().unwrap().nth(1).unwrap().to_string(); + let url: String = ["https://dummyurl.com/", &name].concat(); + + env.assets("ASSETS") + .expect("ASSETS BINDING") + .fetch(url, None) + .await? + .try_into() +} diff --git a/test/src/auto_response.rs b/test/src/auto_response.rs new file mode 100644 index 00000000..0582f3bd --- /dev/null +++ b/test/src/auto_response.rs @@ -0,0 +1,48 @@ +use worker::{durable_object, Env, Request, Response, Result, State, WebSocketRequestResponsePair}; + +#[durable_object] +pub struct AutoResponseObject { + state: State, +} + +impl DurableObject for AutoResponseObject { + fn new(state: State, _env: Env) -> Self { + Self { state } + } + + async fn fetch(&self, req: Request) -> Result { + match req.path().as_str() { + "/set" => { + // Configure ping -> pong auto-response for all websockets bound to this DO. + let pair = WebSocketRequestResponsePair::new("ping", "pong")?; + self.state.set_websocket_auto_response(&pair); + Response::ok("ok") + } + "/get" => { + if let Some(pair) = self.state.get_websocket_auto_response() { + let request_str = pair.request(); + let response_str = pair.response(); + Response::ok(format!("{request_str}:{response_str}")) + } else { + Response::ok("none") + } + } + _ => Response::error("Not Found", 404), + } + } +} + +// Route handler to exercise the Durable Object from tests. +#[worker::send] +pub async fn handle_auto_response( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace = env.durable_object("AUTO")?; + let stub = namespace.id_from_name("singleton")?.get_stub()?; + // Ensure auto-response is configured + stub.fetch_with_str("https://fake-host/set").await?; + // Retrieve and return it for assertion + stub.fetch_with_str("https://fake-host/get").await +} diff --git a/worker-sandbox/src/cache.rs b/test/src/cache.rs similarity index 94% rename from worker-sandbox/src/cache.rs rename to test/src/cache.rs index 91caefee..dcb73c67 100644 --- a/worker-sandbox/src/cache.rs +++ b/test/src/cache.rs @@ -1,13 +1,13 @@ use super::SomeSharedData; use futures_util::stream::StreamExt; use rand::Rng; -use std::time::Duration; +use std::{borrow::ToOwned, time::Duration}; use worker::{console_log, Cache, Date, Delay, Env, Request, Response, ResponseBuilder, Result}; fn key(req: &Request) -> Result> { let uri = req.url()?; let mut segments = uri.path_segments().unwrap(); - Ok(segments.nth(2).map(|s| s.to_owned())) + Ok(segments.nth(2).map(ToOwned::to_owned)) } #[worker::send] @@ -44,9 +44,8 @@ pub async fn handle_cache_api_get( let cache = Cache::default(); if let Some(resp) = cache.get(format!("https://{key}/"), true).await? { return Ok(resp); - } else { - return Response::ok("cache miss"); } + return Response::ok("cache miss"); } Response::error("key missing", 400) } @@ -99,8 +98,8 @@ pub async fn handle_cache_stream( Ok(resp) } else { console_log!("Cache MISS!"); - let mut rng = rand::thread_rng(); - let count = rng.gen_range(0..10); + let mut rng = rand::rng(); + let count = rng.random_range(0..10); let stream = futures_util::stream::repeat("Hello, world!\n") .take(count) .then(|text| async move { diff --git a/test/src/container.rs b/test/src/container.rs new file mode 100644 index 00000000..44dd8fb0 --- /dev/null +++ b/test/src/container.rs @@ -0,0 +1,144 @@ +#[cfg(feature = "http")] +use std::convert::TryInto; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; +use std::time::Duration; + +use futures_util::StreamExt; +use wasm_bindgen::{throw_str, UnwrapThrowExt}; +use wasm_bindgen_futures::spawn_local; +use worker::*; + +use crate::SomeSharedData; + +#[durable_object] +pub struct EchoContainer { + state: State, + ready: Arc, +} + +impl DurableObject for EchoContainer { + fn new(state: State, _env: Env) -> Self { + let container = state.container().expect_throw("failed to get container"); + if !container.running() { + container + .start(None) + .expect_throw("failed to start container"); + } + let ready = Arc::new(AtomicBool::new(false)); + let ready_clone = Arc::clone(&ready); + spawn_local(async move { + for _ in 0..10 { + match container + .get_tcp_port(8080) + .expect_throw("failed to get tcp port 8080") + .fetch("https://github.com/service/http://container.miniflare.mocks/ping", None) + .await + { + Ok(resp) => { + #[cfg(feature = "http")] + if !resp.status().is_success() { + throw_str("failed to fetch ping: bad status"); + } + #[cfg(not(feature = "http"))] + if !(200..300).contains(&resp.status_code()) { + throw_str("failed to fetch ping: bad status"); + } + ready_clone.store(true, Ordering::Release); + return; + } + Err(e) if e.to_string().contains("container port not found") => { + Delay::from(Duration::from_millis(300)).await; + continue; + } + Err(e) => throw_str(&format!("failed to fetch ping: {}", e)), + } + } + throw_str("failed to fetch ping"); + }); + Self { state, ready } + } + + async fn fetch(&self, req: Request) -> Result { + while !self.ready.load(Ordering::Acquire) { + Delay::from(Duration::from_millis(100)).await; + } + match self.state.container() { + Some(container) => match container.get_tcp_port(8080)?.fetch_request(req).await { + Ok(resp) => { + #[cfg(feature = "http")] + let resp = resp.try_into()?; + Ok(resp) + } + Err(e) => Err(e), + }, + None => Response::error("No container", 500), + } + } +} + +const CONTAINER_NAME: &str = "my-container"; + +#[worker::send] +pub async fn handle_container( + mut req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let namespace = env.durable_object("ECHO_CONTAINER")?; + let id = namespace.id_from_name(CONTAINER_NAME)?; + let stub = id.get_stub()?; + match req.method() { + Method::Post => { + let body = req.text().await?; + let req = Request::new_with_init( + "http://container.miniflare.mocks/echo", + RequestInit::new() + .with_method(Method::Post) + .with_body(Some(body.into())), + )?; + stub.fetch_with_request(req).await + } + Method::Get => { + let WebSocketPair { server, client } = WebSocketPair::new()?; + server.accept()?; + let mut req = Request::new("http://container.miniflare.mocks/ws", Method::Get)?; + req.headers_mut()?.set("upgrade", "websocket")?; + let resp = stub.fetch_with_request(req).await?; + let ws = match resp.websocket() { + Some(ws) => ws, + None => return Response::error("Expected websocket response", 500), + }; + ws.accept()?; + spawn_local(redir_websocket(ws, server)); + Response::from_websocket(client) + } + _ => Response::error("Container method not allowed", 405), + } +} + +async fn redir_websocket(dst: WebSocket, src: WebSocket) { + let mut src_events = src.events().expect_throw("could not open src events"); + let mut dst_events = dst.events().expect_throw("could not open dst events"); + + while let Some(event) = src_events.next().await { + match event.expect_throw("received error in src websocket") { + WebsocketEvent::Message(msg) => { + dst.send_with_str(msg.text().expect_throw("expect a text message from src")) + .expect_throw("failed to send to dst"); + if let Some(Ok(WebsocketEvent::Message(msg))) = dst_events.next().await { + src.send_with_str(msg.text().expect_throw("expect a text message from dst")) + .expect_throw("failed to send to src"); + } else { + throw_str("expect a text message from dst"); + } + } + WebsocketEvent::Close(reason) => { + dst.close(Some(reason.code()), Some(reason.reason())) + .expect_throw("failed to close dst websocket"); + } + } + } +} diff --git a/test/src/counter.rs b/test/src/counter.rs new file mode 100644 index 00000000..f1484754 --- /dev/null +++ b/test/src/counter.rs @@ -0,0 +1,173 @@ +use std::cell::RefCell; +use tokio_stream::{StreamExt, StreamMap}; +use worker::{ + durable_object, wasm_bindgen, wasm_bindgen_futures, Env, Error, Method, Request, Response, + ResponseBuilder, Result, State, WebSocket, WebSocketIncomingMessage, WebSocketPair, + WebsocketEvent, +}; + +use crate::SomeSharedData; + +#[durable_object] +pub struct Counter { + count: RefCell, + unstored_count: RefCell, + state: State, + initialized: RefCell, + env: Env, +} + +impl DurableObject for Counter { + fn new(state: State, env: Env) -> Self { + Self { + count: RefCell::new(0), + unstored_count: RefCell::new(0), + initialized: RefCell::new(false), + state, + env, + } + } + + async fn fetch(&self, req: Request) -> Result { + if !*self.initialized.borrow() { + *self.initialized.borrow_mut() = true; + *self.count.borrow_mut() = self.state.storage().get("count").await.unwrap_or(0); + } + + if req.path().eq("/ws") { + let pair = WebSocketPair::new()?; + let server = pair.server; + // accept websocket with hibernation api + self.state.accept_web_socket(&server); + server + .serialize_attachment("hello") + .expect("failed to serialize attachment"); + + return Ok(ResponseBuilder::new() + .with_status(101) + .with_websocket(pair.client) + .empty()); + } + + *self.unstored_count.borrow_mut() += 1; + *self.count.borrow_mut() += 10; + let count = *self.count.borrow(); + self.state.storage().put("count", count).await?; + + Response::ok(format!( + "[durable_object]: self.count: {}, self.unstored_count: {}, secret value: {}", + self.count.borrow(), + self.unstored_count.borrow(), + self.env.secret("SOME_SECRET")? + )) + } + + async fn websocket_message( + &self, + ws: WebSocket, + _message: WebSocketIncomingMessage, + ) -> Result<()> { + let _attach: String = ws + .deserialize_attachment()? + .expect("websockets should have an attachment"); + // get and increment storage by 10 + let mut count: usize = self.state.storage().get("count").await.unwrap_or(0); + count += 10; + self.state.storage().put("count", count).await?; + // send value to client + ws.send_with_str(format!("{count}")) + .expect("failed to send value to client"); + Ok(()) + } + + async fn websocket_close( + &self, + _ws: WebSocket, + _code: usize, + _reason: String, + _was_clean: bool, + ) -> Result<()> { + Ok(()) + } + + async fn websocket_error(&self, _ws: WebSocket, _error: Error) -> Result<()> { + Ok(()) + } +} + +#[worker::send] +pub async fn handle_id(req: Request, env: Env, _data: SomeSharedData) -> Result { + let durable_object_name = if req.path().contains("shared") { + "SHARED_COUNTER" + } else { + "COUNTER" + }; + let namespace = env.durable_object(durable_object_name).expect("DAWJKHDAD"); + let stub = namespace.id_from_name("A")?.get_stub()?; + // when calling fetch to a Durable Object, a full URL must be used. Alternatively, a + // compatibility flag can be provided in wrangler.toml to opt-in to older behavior: + // https://developers.cloudflare.com/workers/platform/compatibility-dates#durable-object-stubfetch-requires-a-full-url + stub.fetch_with_str("https://fake-host/").await +} + +#[worker::send] +pub async fn handle_websocket(req: Request, env: Env, _data: SomeSharedData) -> Result { + let durable_object_name = if req.path().contains("shared") { + "SHARED_COUNTER" + } else { + "COUNTER" + }; + // Accept / handle a websocket connection + let pair = WebSocketPair::new()?; + let server = pair.server; + server.accept()?; + + // Connect to Durable Object via WS + let namespace = env + .durable_object(durable_object_name) + .expect("failed to get namespace"); + let stub = namespace.id_from_name("A")?.get_stub()?; + let mut req = Request::new("https://fake-host/ws", Method::Get)?; + req.headers_mut()?.set("upgrade", "websocket")?; + + let res = stub.fetch_with_request(req).await?; + let do_ws = res.websocket().expect("server did not accept websocket"); + do_ws.accept()?; + + wasm_bindgen_futures::spawn_local(async move { + let event_stream = server.events().expect("could not open stream"); + let do_event_stream = do_ws.events().expect("could not open stream"); + + let mut map = StreamMap::new(); + map.insert("client", event_stream); + map.insert("durable", do_event_stream); + + while let Some((key, event)) = map.next().await { + match key { + "client" => match event.expect("received error in websocket") { + WebsocketEvent::Message(msg) => { + if let Some(text) = msg.text() { + do_ws.send_with_str(text).expect("could not relay text"); + } + } + WebsocketEvent::Close(_) => { + let _res = do_ws.close(Some(1000), Some("client closed".to_string())); + } + }, + "durable" => match event.expect("received error in websocket") { + WebsocketEvent::Message(msg) => { + if let Some(text) = msg.text() { + server.send_with_str(text).expect("could not relay text"); + } + } + WebsocketEvent::Close(_) => { + let _res = server.close(Some(1000), Some("durable closed".to_string())); + } + }, + _ => unreachable!(), + } + } + }); + + Response::from_websocket(pair.client) +} diff --git a/test/src/d1.rs b/test/src/d1.rs new file mode 100644 index 00000000..63ce8635 --- /dev/null +++ b/test/src/d1.rs @@ -0,0 +1,301 @@ +use crate::SomeSharedData; +use crate::{ + js_sys::{Object, Reflect}, + wasm_bindgen, +}; +use serde::Deserialize; +use wasm_bindgen::JsValue; +use worker::{D1PreparedArgument, D1Type, Env, Error, Request, Response, Result}; + +#[derive(Deserialize)] +struct Person { + id: u32, + name: String, + age: u32, +} + +#[worker::send] +pub async fn prepared_statement( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let db = env.d1("DB")?; + let unbound_stmt = worker::query!(&db, "SELECT * FROM people WHERE name = ?"); + + let stmt = unbound_stmt.bind_refs(&D1Type::Text("Ryan Upton"))?; + + // All rows + let results = stmt.all().await?; + let people = results.results::()?; + + assert!(results.success()); + assert_eq!(results.error(), None); + assert_eq!(people.len(), 1); + assert_eq!(people[0].name, "Ryan Upton"); + assert_eq!(people[0].age, 21); + assert_eq!(people[0].id, 6); + + // All columns of the first rows + let person = stmt.first::(None).await?.unwrap(); + assert_eq!(person.name, "Ryan Upton"); + assert_eq!(person.age, 21); + + // The name of the first row + let name = stmt.first::(Some("name")).await?.unwrap(); + assert_eq!(name, "Ryan Upton"); + + // All of the rows as column arrays of raw JSON values. + let rows = stmt.raw::().await?; + assert_eq!(rows.len(), 1); + let columns = &rows[0]; + + assert_eq!(columns[0].as_u64(), Some(6)); + assert_eq!(columns[1].as_str(), Some("Ryan Upton")); + assert_eq!(columns[2].as_u64(), Some(21)); + + let stmt_2 = unbound_stmt.bind_refs([&D1Type::Text("John Smith")])?; + let person = stmt_2.first::(None).await?.unwrap(); + assert_eq!(person.name, "John Smith"); + assert_eq!(person.age, 92); + + let prepared_argument = D1PreparedArgument::new(&D1Type::Text("Dorian Fischer")); + let stmt_3 = unbound_stmt.bind_refs(&prepared_argument)?; + let person = stmt_3.first::(None).await?.unwrap(); + assert_eq!(person.name, "Dorian Fischer"); + assert_eq!(person.age, 19); + + Response::ok("ok") +} + +#[worker::send] +pub async fn batch(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let db = env.d1("DB")?; + let mut results = db + .batch(vec![ + worker::query!(&db, "SELECT * FROM people WHERE id < 4"), + worker::query!(&db, "SELECT * FROM people WHERE id > 4"), + ]) + .await? + .into_iter(); + + let first_results = results.next().unwrap().results::()?; + assert_eq!(first_results.len(), 3); + assert_eq!(first_results[0].id, 1); + assert_eq!(first_results[1].id, 2); + assert_eq!(first_results[2].id, 3); + + let second_results = results.next().unwrap().results::()?; + assert_eq!(second_results.len(), 2); + assert_eq!(second_results[0].id, 5); + assert_eq!(second_results[1].id, 6); + + Response::ok("ok") +} + +#[worker::send] +pub async fn exec(mut req: Request, env: Env, _data: SomeSharedData) -> Result { + let db = env.d1("DB")?; + let result = db + .exec(req.text().await?.as_ref()) + .await + .expect("doesn't exist"); + + Response::ok(result.count()?.unwrap_or_default().to_string()) +} + +#[worker::send] +pub async fn dump(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let db = env.d1("DB")?; + let bytes = db.dump().await?; + Response::from_bytes(bytes) +} + +#[worker::send] +pub async fn error(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let db = env.d1("DB")?; + let error = db + .exec("THIS IS NOT VALID SQL") + .await + .expect_err("did not get error"); + + if let Error::D1(error) = error { + assert_eq!( + error.cause(), + "Error in line 1: THIS IS NOT VALID SQL: near \"THIS\": syntax error at offset 0: SQLITE_ERROR" + ); + } else { + panic!("expected D1 error"); + } + + Response::ok("") +} + +#[derive(Debug, Deserialize)] +struct NullablePerson { + id: u32, + name: Option, + age: Option, +} + +#[worker::send] +pub async fn jsvalue_null_is_null( + _req: Request, + _env: Env, + _data: SomeSharedData, +) -> Result { + assert!(wasm_bindgen::JsValue::NULL.is_null()); + + Response::ok("ok") +} + +#[worker::send] +pub async fn serialize_optional_none( + _req: Request, + _env: Env, + _data: SomeSharedData, +) -> Result { + let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true); + + let none: Option = None; + let js_none = ::serde::ser::Serialize::serialize(&none, &serializer).unwrap(); + assert!(js_none.is_null()); + + Response::ok("ok") +} + +#[worker::send] +pub async fn serialize_optional_some( + _req: Request, + _env: Env, + _data: SomeSharedData, +) -> Result { + let serializer = serde_wasm_bindgen::Serializer::new().serialize_missing_as_null(true); + + let some: Option = Some("Hello".to_string()); + let js_some = ::serde::ser::Serialize::serialize(&some, &serializer).unwrap(); + assert!(js_some.is_string()); + + Response::ok("ok") +} + +#[worker::send] +pub async fn deserialize_optional_none( + _req: Request, + _env: Env, + _data: SomeSharedData, +) -> Result { + let js_value = Object::new(); + Reflect::set(&js_value, &JsValue::from_str("id"), &JsValue::from_f64(1.0)).unwrap(); + Reflect::set(&js_value, &JsValue::from_str("name"), &JsValue::NULL).unwrap(); + Reflect::set(&js_value, &JsValue::from_str("age"), &JsValue::NULL).unwrap(); + + let js_value: JsValue = js_value.into(); + + let value: NullablePerson = serde_wasm_bindgen::from_value(js_value).unwrap(); + + assert_eq!(value.id, 1); + assert_eq!(value.name, None); + assert_eq!(value.age, None); + + Response::ok("ok") +} + +#[worker::send] +pub async fn insert_and_retrieve_optional_none( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let db = env.d1("DB")?; + + let query = worker::query!( + &db, + "INSERT INTO nullable_people (id, name, age) VALUES (?1, ?2, ?3)", + &3, + &None::, + &None:: + )?; + query.run().await?; + + let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 3"); + let person = stmt.first::(None).await?.unwrap(); + assert_eq!(person.id, 3); + assert_eq!(person.name, None); + assert_eq!(person.age, None); + + Response::ok("ok") +} + +#[worker::send] +pub async fn insert_and_retrieve_optional_some( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let db = env.d1("DB")?; + let query = worker::query!( + &db, + "INSERT INTO nullable_people (id, name, age) VALUES (?1, ?2, ?3)", + &4, + &"Dude", + &12 + )?; + query.run().await?; + + let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 4"); + let person = stmt.first::(None).await?.unwrap(); + assert_eq!(person.id, 4); + assert_eq!(person.name, Some("Dude".to_string())); + assert_eq!(person.age, Some(12)); + + Response::ok("ok") +} + +#[worker::send] +pub async fn retrieve_optional_none( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let db = env.d1("DB")?; + + let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 1"); + let person = stmt.first::(None).await?.unwrap(); + assert_eq!(person.id, 1); + assert_eq!(person.name, None); + assert_eq!(person.age, None); + + Response::ok("ok") +} + +#[worker::send] +pub async fn retrieve_optional_some( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let db = env.d1("DB")?; + + let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 2"); + let person = stmt.first::(None).await?.unwrap(); + assert_eq!(person.id, 2); + assert_eq!(person.name, Some("Wynne Ogley".to_string())); + assert_eq!(person.age, Some(67)); + + Response::ok("ok") +} + +#[worker::send] +pub async fn retrive_first_none( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let db = env.d1("DB")?; + + let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 9999"); + assert!(stmt.first::(None).await?.is_none()); + + Response::ok("ok") +} diff --git a/test/src/durable.rs b/test/src/durable.rs new file mode 100644 index 00000000..07797975 --- /dev/null +++ b/test/src/durable.rs @@ -0,0 +1,313 @@ +use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::{cell::RefCell, collections::HashMap}; + +use worker::{ + durable_object, + js_sys::{self, Uint8Array}, + wasm_bindgen::JsValue, + Env, Method, ObjectNamespace, Request, RequestInit, Response, Result, State, +}; + +#[durable_object] +pub struct MyClass { + state: State, + number: RefCell, +} + +#[derive(Deserialize)] +pub struct QueryParams { + name: String, +} + +impl DurableObject for MyClass { + fn new(state: State, _env: Env) -> Self { + // Unfortunately we can't access the `name` property within the Durable Object (see ). Instead, we can pass it as a request parameter. + assert!(state.id().name().is_none()); + Self { + state, + number: RefCell::new(0), + } + } + + #[allow(clippy::too_many_lines)] + async fn fetch(&self, req: Request) -> Result { + let handler = async move { + match req.path().as_str() { + "/hello" => { + let name = &req.query::()?.name; + Response::ok(format!("Hello from {name}!")) + } + "/storage" => { + let storage = self.state.storage(); + let map = [("one".to_string(), 1), ("two".to_string(), 2)] + .iter() + .cloned() + .collect::>(); + storage.put("map", map.clone()).await?; + storage.put("array", [("one", 1), ("two", 2)]).await?; + storage.put("anything", Some(45)).await?; + + let list = storage.list().await?; + let mut keys = vec![]; + + for key in list.keys() { + let key = key? + .as_string() + .ok_or_else(|| "Key wasn't a string".to_string())?; + if key != "count" { + keys.push(key); + } + } + + assert_eq!( + keys, + vec!["anything", "array", "map"], + "Didn't list all of the keys: {keys:?}" + ); + let vals = storage + .get_multiple(keys) + .await + .map_err(|e| e.to_string() + " -- get_multiple")?; + assert_eq!( + serde_wasm_bindgen::from_value::>( + vals.get(&"anything".into()) + )?, + Some(45), + "Didn't get the right Option using get_multiple" + ); + assert_eq!( + serde_wasm_bindgen::from_value::<[(String, i32); 2]>( + vals.get(&"array".into()) + )?, + [("one".to_string(), 1), ("two".to_string(), 2)], + "Didn't get the right array using get_multiple" + ); + assert_eq!( + serde_wasm_bindgen::from_value::>( + vals.get(&"map".into()) + )?, + map, + "Didn't get the right HashMap using get_multiple" + ); + + { + let bytes = Uint8Array::new_with_length(3); + bytes.copy_from(b"123"); + storage.put_raw("bytes", bytes).await?; + let bytes = storage.get::>("bytes").await?; + storage.delete("bytes").await?; + assert_eq!( + bytes, b"123", + "eficient serialization of bytes is not preserved" + ); + } + + #[allow(clippy::items_after_statements)] + #[derive(Serialize)] + struct Stuff { + thing: String, + other: i32, + } + storage + .put_multiple(Stuff { + thing: "Hello there".to_string(), + other: 56, + }) + .await?; + + assert_eq!( + storage.get::("thing").await?, + "Hello there", + "Didn't put the right thing with put_multiple" + ); + assert_eq!( + storage.get::("other").await?, + 56, + "Didn't put the right thing with put_multiple" + ); + + storage.delete_multiple(vec!["thing", "other"]).await?; + + { + const BAR: &[u8] = b"bar"; + let obj = js_sys::Object::new(); + let value = Uint8Array::new_with_length(u32::try_from(BAR.len()).unwrap()); + value.copy_from(BAR); + js_sys::Reflect::set(&obj, &JsValue::from_str("foo"), &value.into())?; + storage.put_multiple_raw(obj).await?; + assert_eq!( + storage.get::>("foo").await?, + BAR, + "Didn't the right thing with put_multiple_raw" + ); + } + + *self.number.borrow_mut() = storage.get("count").await.unwrap_or(0) + 1; + + storage.delete_all().await?; + + let count = *self.number.borrow(); + storage.put("count", count).await?; + Response::ok(self.number.borrow().to_string()) + } + "/transaction" => { + Response::error("transactional storage API is still unstable", 501) + } + _ => Response::error("Not Found", 404), + } + }; + handler + .await + .or_else(|err| Response::error(err.to_string(), 500)) + } +} + +// Route handlers to exercise the Durable Object from tests. +#[worker::send] +pub async fn handle_hello( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace = env.durable_object("MY_CLASS")?; + let name = "my-durable-object"; + let id = namespace.id_from_name(name)?; + let stub = id.get_stub()?; + stub.fetch_with_str(&format!("https://fake-host/hello?name={name}")) + .await +} + +#[worker::send] +pub async fn handle_hello_unique( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace = env.durable_object("MY_CLASS")?; + let id = namespace.unique_id()?; + let name = id.to_string(); + let stub = id.get_stub()?; + stub.fetch_with_str(&format!("https://fake-host/hello?name={name}")) + .await +} + +#[worker::send] +pub async fn handle_storage( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace = env.durable_object("MY_CLASS")?; + let stub = namespace.id_from_name("singleton")?.get_stub()?; + stub.fetch_with_str("https://fake-host/storage").await +} + +#[worker::send] +pub async fn handle_basic_test( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace: ObjectNamespace = env.durable_object("MY_CLASS")?; + let id = namespace.id_from_name("A")?; + assert_eq!(id.name(), Some("A".into()), "Missing name"); + assert!( + namespace.unique_id()?.name().is_none(), + "Expected name property to be absent" + ); + let bad = env.durable_object("DFSDF_FAKE_BINDING"); + assert!(bad.is_err(), "Invalid binding did not raise error"); + + let stub = id.get_stub()?; + let res = stub + .fetch_with_str(&format!( + "https://fake-host/hello?name={}", + id.name().unwrap() + )) + .await? + .text() + .await?; + let res2 = stub + .fetch_with_request(Request::new_with_init( + &format!("https://fake-host/hello?name={}", id.name().unwrap()), + RequestInit::new() + .with_body(Some("lol".into())) + .with_method(Method::Post), + )?) + .await? + .text() + .await?; + + assert_eq!(res, res2, "Durable object responded wrong to 'hello'"); + + let res = stub + .fetch_with_str("https://fake-host/storage") + .await? + .text() + .await?; + let num = res + .parse::() + .map_err(|_| "Durable Object responded wrong to 'storage': ".to_string() + &res)?; + let res = stub + .fetch_with_str("https://fake-host/storage") + .await? + .text() + .await?; + let num2 = res + .parse::() + .map_err(|_| "Durable Object responded wrong to 'storage'".to_string())?; + + assert_eq!(num2, num + 1, "Durable object responded wrong to 'storage'"); + + let res = stub + .fetch_with_str("https://fake-host/transaction") + .await? + .text() + .await?; + let num = res + .parse::() + .map_err(|_| "Durable Object responded wrong to 'transaction': ".to_string() + &res)?; + + assert_eq!( + num, + num2 + 1, + "Durable object responded wrong to 'transaction'" + ); + + Response::ok("ok") +} + +#[worker::send] +pub async fn handle_get_by_name( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace = env.durable_object("MY_CLASS")?; + let name = "my-durable-object"; + + // Using the new get_by_name method - this is equivalent to: + // let id = namespace.id_from_name(name)?; + // let stub = id.get_stub()?; + let stub = namespace.get_by_name(name)?; + + stub.fetch_with_str(&format!("https://fake-host/hello?name={name}")) + .await +} + +#[worker::send] +pub async fn handle_get_by_name_with_location_hint( + _req: Request, + env: Env, + _data: crate::SomeSharedData, +) -> Result { + let namespace = env.durable_object("MY_CLASS")?; + let name = "my-durable-object"; + + // Using the new get_by_name_with_location_hint method + let stub = namespace.get_by_name_with_location_hint(name, "enam")?; + + stub.fetch_with_str(&format!("https://fake-host/hello?name={name}")) + .await +} diff --git a/worker-sandbox/src/fetch.rs b/test/src/fetch.rs similarity index 100% rename from worker-sandbox/src/fetch.rs rename to test/src/fetch.rs diff --git a/worker-sandbox/src/form.rs b/test/src/form.rs similarity index 90% rename from worker-sandbox/src/form.rs rename to test/src/form.rs index bccff11f..4c6ad57f 100644 --- a/worker-sandbox/src/form.rs +++ b/test/src/form.rs @@ -2,9 +2,8 @@ use super::SomeSharedData; use blake2::Blake2b512; use blake2::Digest; use serde::{Deserialize, Serialize}; -use worker::kv; -use worker::{Env, Request, Result}; -use worker::{FormEntry, Response}; +use std::convert::TryFrom; +use worker::{kv, Env, FormEntry, Request, Response, Result}; #[worker::send] pub async fn handle_formdata_name( @@ -36,7 +35,7 @@ pub async fn handle_formdata_name( if let Some(value) = form.get(NAME) { match value { FormEntry::Field(v) => Response::from_json(&serde_json::json!({ NAME: v })), - _ => bad_request, + FormEntry::File(_) => bad_request, } } else { bad_request @@ -66,7 +65,7 @@ pub async fn handle_formdata_file_size( let b = file.bytes().await?; let record = FileSize { name: file.name(), - size: b.len() as u32, + size: u32::try_from(b.len()).unwrap(), }; // hash the file, and use result as the key @@ -82,7 +81,7 @@ pub async fn handle_formdata_file_size( // list the default number of keys from the namespace Response::from_json(&kv.list().execute().await?.keys) } - _ => Response::error("Bad Request", 400), + FormEntry::Field(_) => Response::error("Bad Request", 400), }; } @@ -122,7 +121,7 @@ pub async fn handle_is_secret( let val = env.secret(&name)?; return Response::ok(val.to_string()); } - _ => return Response::error("Bad Request", 400), + FormEntry::File(_) => return Response::error("Bad Request", 400), }; } diff --git a/test/src/js_snippets.rs b/test/src/js_snippets.rs new file mode 100644 index 00000000..ca929da8 --- /dev/null +++ b/test/src/js_snippets.rs @@ -0,0 +1,40 @@ +use wasm_bindgen::prelude::*; +use worker::*; + +use crate::SomeSharedData; + +#[wasm_bindgen(inline_js = "export function js_performance_now() { return performance.now(); }")] +extern "C" { + fn js_performance_now() -> f64; +} + +#[wasm_bindgen( + inline_js = "export function js_console_log(value) { console.log(\"first log:\", value); }" +)] +extern "C" { + fn js_console_log(value: String); +} + +#[wasm_bindgen( + inline_js = "export function js_throw_error() { throw new Error('Intentional JS error for testing recovery'); }" +)] +extern "C" { + fn js_throw_error(); +} + +#[worker::send] +pub async fn performance_now(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + Response::ok(format!("now: {}", js_performance_now())) +} + +#[worker::send] +pub async fn console_log(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + js_console_log("test".to_owned()); + Response::ok("OK") +} + +pub fn throw_js_error(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + // This will call the JS function which throws an error + js_throw_error(); + Response::ok("This should never be reached") +} diff --git a/worker-sandbox/src/kv.rs b/test/src/kv.rs similarity index 98% rename from worker-sandbox/src/kv.rs rename to test/src/kv.rs index 44e7c3da..55533aae 100644 --- a/worker-sandbox/src/kv.rs +++ b/test/src/kv.rs @@ -95,7 +95,7 @@ pub async fn put_metadata(_req: Request, env: Env, _data: SomeSharedData) -> Res #[worker::send] pub async fn put_expiration(_req: Request, env: Env, _data: SomeSharedData) -> Result { - const EXPIRATION: u64 = 2000000000; + const EXPIRATION: u64 = 2_000_000_000; let store = env.kv(TEST_NAMESPACE)?; store .put("put_c", "test")? diff --git a/worker-sandbox/src/lib.rs b/test/src/lib.rs similarity index 69% rename from worker-sandbox/src/lib.rs rename to test/src/lib.rs index 0441e969..4f28ca70 100644 --- a/worker-sandbox/src/lib.rs +++ b/test/src/lib.rs @@ -1,38 +1,44 @@ +use regex::Regex; use serde::{Deserialize, Serialize}; use std::sync::{ atomic::{AtomicBool, Ordering}, - Mutex, + LazyLock, Mutex, }; #[cfg(feature = "http")] use tower_service::Service; -use worker::*; +#[cfg(feature = "http")] +use worker::HttpRequest; +use worker::{console_log, event, js_sys, wasm_bindgen, Env, Result}; +#[cfg(not(feature = "http"))] +use worker::{Request, Response}; + mod alarm; +mod analytics_engine; +mod assets; +mod auto_response; mod cache; +mod container; mod counter; mod d1; +mod durable; mod fetch; mod form; +mod js_snippets; mod kv; +mod put_raw; mod queue; mod r2; +mod rate_limit; mod request; mod router; +mod secret_store; mod service; mod socket; -mod test; +mod sql_counter; +mod sql_iterator; mod user; -mod utils; mod ws; -#[derive(Deserialize, Serialize)] -struct MyData { - message: String, - #[serde(default)] - is: bool, - #[serde(default)] - data: Vec, -} - #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct ApiData { @@ -43,20 +49,20 @@ struct ApiData { #[derive(Clone)] pub struct SomeSharedData { - regex: regex::Regex, + regex: &'static Regex, } static GLOBAL_STATE: AtomicBool = AtomicBool::new(false); static GLOBAL_QUEUE_STATE: Mutex> = Mutex::new(Vec::new()); +static DATA_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap()); + // We're able to specify a start event that is called when the WASM is initialized before any // requests. This is useful if you have some global state or setup code, like a logger. This is // only called once for the entire lifetime of the worker. #[event(start)] pub fn start() { - utils::set_panic_hook(); - // Change some global state so we know that we ran our setup function. GLOBAL_STATE.store(true, Ordering::SeqCst); } @@ -70,15 +76,18 @@ type HandlerResponse = http::Response; #[cfg(not(feature = "http"))] type HandlerResponse = Response; -#[event(fetch)] +/// Entrypoint to the worker for handling fetch requests. +/// +/// # Errors +/// +/// Returns the same error as the underlying router implementation. +#[event(fetch, respond_with_errors)] pub async fn main( request: HandlerRequest, env: Env, _ctx: worker::Context, ) -> Result { - let data = SomeSharedData { - regex: regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(), - }; + let data = SomeSharedData { regex: &DATA_REGEX }; #[cfg(feature = "http")] let res = { diff --git a/worker-sandbox/src/test/put_raw.rs b/test/src/put_raw.rs similarity index 53% rename from worker-sandbox/src/test/put_raw.rs rename to test/src/put_raw.rs index 38e5835e..1ad86975 100644 --- a/worker-sandbox/src/test/put_raw.rs +++ b/test/src/put_raw.rs @@ -1,4 +1,10 @@ -use worker::{js_sys::Uint8Array, wasm_bindgen::JsValue, *}; +use std::convert::TryFrom; +use worker::{ + durable_object, js_sys, js_sys::Uint8Array, wasm_bindgen, wasm_bindgen::JsValue, Env, Request, + Response, Result, State, +}; + +use crate::SomeSharedData; #[durable_object] pub struct PutRawTestObject { @@ -6,8 +12,8 @@ pub struct PutRawTestObject { } impl PutRawTestObject { - async fn put_raw(&mut self) -> Result<()> { - let mut storage = self.state.storage(); + async fn put_raw(&self) -> Result<()> { + let storage = self.state.storage(); let bytes = Uint8Array::new_with_length(3); bytes.copy_from(b"123"); storage.put_raw("bytes", bytes).await?; @@ -20,11 +26,11 @@ impl PutRawTestObject { Ok(()) } - async fn put_multiple_raw(&mut self) -> Result<()> { - let mut storage = self.state.storage(); - let obj = js_sys::Object::new(); + async fn put_multiple_raw(&self) -> Result<()> { const BAR: &[u8] = b"bar"; - let value = Uint8Array::new_with_length(BAR.len() as _); + let storage = self.state.storage(); + let obj = js_sys::Object::new(); + let value = Uint8Array::new_with_length(u32::try_from(BAR.len()).unwrap()); value.copy_from(BAR); js_sys::Reflect::set(&obj, &JsValue::from_str("foo"), &value.into())?; storage.put_multiple_raw(obj).await?; @@ -32,23 +38,34 @@ impl PutRawTestObject { assert_eq!( storage.get::>("foo").await?, BAR, - "Didn't the right thing with put_multiple_raw" + "Didn't get the right thing with put_multiple_raw" ); Ok(()) } } -#[durable_object] impl DurableObject for PutRawTestObject { fn new(state: State, _env: Env) -> Self { Self { state } } - async fn fetch(&mut self, _: Request) -> Result { + async fn fetch(&self, _: Request) -> Result { self.put_raw().await?; self.put_multiple_raw().await?; Response::ok("ok") } } + +#[worker::send] +pub(crate) async fn handle_put_raw( + req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let namespace = env.durable_object("PUT_RAW_TEST_OBJECT")?; + let id = namespace.unique_id()?; + let stub = id.get_stub()?; + stub.fetch_with_request(req).await +} diff --git a/worker-sandbox/src/queue.rs b/test/src/queue.rs similarity index 92% rename from worker-sandbox/src/queue.rs rename to test/src/queue.rs index 7e7bfc9e..5e9da4a8 100644 --- a/worker-sandbox/src/queue.rs +++ b/test/src/queue.rs @@ -30,16 +30,14 @@ pub async fn queue(message_batch: MessageBatch, _env: Env, _ctx: Cont pub async fn handle_queue_send(req: Request, env: Env, _data: SomeSharedData) -> Result { let uri = req.url()?; let mut segments = uri.path_segments().unwrap(); - let id = match segments + let Some(id) = segments .nth(2) .map(|id| Uuid::try_parse(id).ok()) .and_then(|u| u) - { - Some(id) => id, - None => { - return Response::error("Failed to parse id, expected a UUID", 400); - } + else { + return Response::error("Failed to parse id, expected a UUID", 400); }; + let my_queue = match env.queue("my_queue") { Ok(queue) => queue, Err(err) => return Response::error(format!("Failed to get queue: {err:?}"), 500), @@ -51,7 +49,7 @@ pub async fn handle_queue_send(req: Request, env: Env, _data: SomeSharedData) -> }) .await { - Ok(_) => Response::ok("Message sent"), + Ok(()) => Response::ok("Message sent"), Err(err) => Response::error(format!("Failed to send message to queue: {err:?}"), 500), } } diff --git a/worker-sandbox/src/r2.rs b/test/src/r2.rs similarity index 90% rename from worker-sandbox/src/r2.rs rename to test/src/r2.rs index 59e67244..5ef6c0f9 100644 --- a/worker-sandbox/src/r2.rs +++ b/test/src/r2.rs @@ -1,6 +1,9 @@ -use std::{collections::HashMap, sync::Mutex}; - use futures_util::StreamExt; +use std::{ + collections::HashMap, + convert::TryFrom, + sync::atomic::{AtomicBool, Ordering}, +}; use worker::{ Bucket, Conditional, Data, Date, Env, FixedLengthStream, HttpMetadata, Include, Request, Response, Result, @@ -8,19 +11,15 @@ use worker::{ use crate::SomeSharedData; -static SEEDED: Mutex = Mutex::new(false); +static SEEDED: AtomicBool = AtomicBool::new(false); pub async fn seed_bucket(bucket: &Bucket) -> Result<()> { - { - let mut seeded = SEEDED.lock().unwrap(); - - if *seeded { - return Ok(()); - } - - *seeded = true; + if SEEDED.load(Ordering::Acquire) { + return Ok(()); } + SEEDED.store(true, Ordering::Release); + bucket.put("no-props", "text".to_string()).execute().await?; bucket .put("no-props-no-body", Data::Empty) @@ -78,8 +77,7 @@ pub async fn list(_req: Request, env: Env, _data: SomeSharedData) -> Result Result R Response::ok("ok") } +#[allow(clippy::large_stack_arrays)] #[worker::send] pub async fn put_multipart(_req: Request, env: Env, _data: SomeSharedData) -> Result { const R2_MULTIPART_CHUNK_MIN_SIZE: usize = 5 * 1_024 * 1_024; // 5MiB. @@ -211,8 +210,12 @@ pub async fn put_multipart(_req: Request, env: Env, _data: SomeSharedData) -> Re ]; let mut uploaded_parts = vec![]; for (chunk_index, chunk_size) in chunk_sizes.iter().copied().enumerate() { - let chunk = vec![chunk_index as u8; chunk_size]; - uploaded_parts.push(upload.upload_part(chunk_index as u16, chunk).await?); + let chunk = vec![u8::try_from(chunk_index).unwrap(); chunk_size]; + uploaded_parts.push( + upload + .upload_part(u16::try_from(chunk_index).unwrap(), chunk) + .await?, + ); } upload.complete(uploaded_parts).await?; @@ -254,6 +257,14 @@ pub async fn delete(_req: Request, env: Env, _data: SomeSharedData) -> Result = (0..1000).map(|i| format!("key_{i}")).collect(); + for key in &keys { + bucket.put(key, Data::Empty).execute().await?; + } + let objects = bucket.list().execute().await?; + assert_eq!(objects.objects().len(), keys.len()); + + bucket.delete_multiple(keys).await?; let objects = bucket.list().execute().await?; assert_eq!(objects.objects().len(), 0); diff --git a/test/src/rate_limit.rs b/test/src/rate_limit.rs new file mode 100644 index 00000000..d8665d1e --- /dev/null +++ b/test/src/rate_limit.rs @@ -0,0 +1,84 @@ +use super::SomeSharedData; +use std::collections::HashMap; +use worker::{js_sys, Env, Request, Response, Result}; + +#[worker::send] +pub async fn handle_rate_limit_check( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let rate_limiter = env.rate_limiter("TEST_RATE_LIMITER")?; + + // Use a fixed key for testing + let outcome = rate_limiter.limit("test-key".to_string()).await?; + + Response::from_json(&serde_json::json!({ + "success": outcome.success, + })) +} + +#[worker::send] +pub async fn handle_rate_limit_with_key( + req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let uri = req.url()?; + let segments = uri.path_segments().unwrap().collect::>(); + let key = segments.get(2).unwrap_or(&"default-key"); + + let rate_limiter = env.rate_limiter("TEST_RATE_LIMITER")?; + let outcome = rate_limiter.limit(key.to_string()).await?; + + Response::from_json(&serde_json::json!({ + "success": outcome.success, + "key": key, + })) +} + +#[worker::send] +pub async fn handle_rate_limit_bulk_test( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let rate_limiter = env.rate_limiter("TEST_RATE_LIMITER")?; + + // Test multiple requests to verify rate limiting behavior + let mut results = Vec::new(); + for i in 0..15 { + let key = format!("bulk-test-{}", i % 3); // Use 3 different keys + let outcome = rate_limiter.limit(key.clone()).await?; + results.push(serde_json::json!({ + "index": i, + "key": key, + "success": outcome.success, + })); + } + + Response::from_json(&serde_json::json!({ + "results": results, + })) +} + +#[worker::send] +pub async fn handle_rate_limit_reset( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let rate_limiter = env.rate_limiter("TEST_RATE_LIMITER")?; + + // Use a unique key to avoid interference with other tests + let key = format!("reset-test-{}", js_sys::Date::now()); + + // Make multiple requests with the same key + let mut outcomes = HashMap::new(); + for i in 0..12 { + let outcome = rate_limiter.limit(key.clone()).await?; + outcomes.insert(format!("request_{}", i + 1), outcome.success); + } + + Response::from_json(&outcomes) +} diff --git a/worker-sandbox/src/request.rs b/test/src/request.rs similarity index 89% rename from worker-sandbox/src/request.rs rename to test/src/request.rs index ccafc902..6b89113b 100644 --- a/worker-sandbox/src/request.rs +++ b/test/src/request.rs @@ -3,17 +3,21 @@ use crate::SomeSharedData; use super::ApiData; use futures_util::StreamExt; use futures_util::TryStreamExt; +use serde::Deserialize; +use serde::Serialize; use std::time::Duration; use worker::Env; use worker::{console_log, Date, Delay, Request, Response, ResponseBody, ResponseBuilder, Result}; + +#[allow(clippy::needless_pass_by_value)] pub fn handle_a_request(req: Request, _env: Env, _data: SomeSharedData) -> Result { Response::ok(format!( "req at: {}, located at: {:?}, within: {}", req.path(), req.cf().map(|cf| cf.coordinates().unwrap_or_default()), - req.cf() - .map(|cf| cf.region().unwrap_or_else(|| "unknown region".into())) - .unwrap_or(String::from("No CF properties")) + req.cf().map_or(String::from("No CF properties"), |cf| cf + .region() + .unwrap_or_else(|| "unknown region".into())) )) } @@ -26,9 +30,9 @@ pub async fn handle_async_request( "[async] req at: {}, located at: {:?}, within: {}", req.path(), req.cf().map(|cf| cf.coordinates().unwrap_or_default()), - req.cf() - .map(|cf| cf.region().unwrap_or_else(|| "unknown region".into())) - .unwrap_or(String::from("No CF properties")) + req.cf().map_or(String::from("No CF properties"), |cf| cf + .region() + .unwrap_or_else(|| "unknown region".into())) )) } @@ -92,6 +96,16 @@ pub async fn handle_var(_req: Request, env: Env, _data: SomeSharedData) -> Resul Response::ok(env.var("SOME_VARIABLE")?.to_string()) } +pub async fn handle_object_var(_req: Request, env: Env, _: SomeSharedData) -> Result { + #[derive(Serialize, Deserialize, PartialEq, Eq)] + struct Obj { + foo: i32, + bar: String, + } + let obj = env.object_var::("SOME_OBJECT_VARIABLE")?; + Response::from_json(&obj) +} + pub async fn handle_bytes(_req: Request, _env: Env, _data: SomeSharedData) -> Result { Response::from_bytes(vec![1, 2, 3, 4, 5, 6, 7]) } @@ -188,7 +202,7 @@ pub async fn handle_cloned_stream( futures_util::stream::repeat(()) .take(10) .enumerate() - .then(|(index, _)| async move { + .then(|(index, ())| async move { Delay::from(Duration::from_millis(100)).await; Result::Ok(index.to_string().into_bytes()) }); diff --git a/test/src/router.rs b/test/src/router.rs new file mode 100644 index 00000000..4ca07033 --- /dev/null +++ b/test/src/router.rs @@ -0,0 +1,330 @@ +use crate::{ + alarm, analytics_engine, assets, auto_response, cache, container, counter, d1, durable, fetch, + form, js_snippets, kv, put_raw, queue, r2, rate_limit, request, secret_store, service, socket, + sql_counter, sql_iterator, user, ws, SomeSharedData, GLOBAL_STATE, +}; +#[cfg(feature = "http")] +use std::convert::TryInto; +use std::sync::atomic::Ordering; + +use worker::{console_log, Env, Fetch, Request, Response, ResponseBuilder, Result}; + +#[cfg(not(feature = "http"))] +use worker::{RouteContext, Router}; + +#[cfg(feature = "http")] +use axum::{ + routing::{delete, get, head, options, patch, post, put}, + Extension, +}; + +// Transform the argument into the correct form for the router. +// For axum::Router: +// - "*arg" -> "{*arg}" +// - "arg" -> "{arg}" +#[cfg(feature = "http")] +macro_rules! transform_arg ( + ($arg_name:literal) => { + format!("{{{}}}", $arg_name) + } +); +// For worker::Router using matchit=0.7. (Note that in matchit=0.8.6 +// (), parameter matching +// is much closer to axum::Router so we should be able to remove all of this +// logic if we update.) +// - "*arg" -> "*arg" +// - "arg" -> ":arg" +#[cfg(not(feature = "http"))] +macro_rules! transform_arg ( + ($arg_name:literal) => { + if $arg_name.starts_with('*') { + format!("{}", $arg_name) + } else { + format!(":{}", $arg_name) + } + }; +); + +// Like `format!`, but apply `transform_arg!` to each argument first. +macro_rules! format_route ( + ($path_format:literal $(, $arg_name:literal)*) => { + &format!($path_format, $( + transform_arg!($arg_name) + ),*) + }; +); + +// Rewrites a handler with legacy http types to use axum extractors / response type. +// Returns an async handler unless the macro is called with a second argument 'sync'. +#[cfg(feature = "http")] +macro_rules! handler ( + ($name:path) => { + |Extension(env): Extension, Extension(data): Extension, req: axum::extract::Request| async { + let resp = $name(req.try_into().expect("convert request"), env, data).await.expect("handler result"); + Into::>::into(resp) + } + }; + ($name:path, sync) => { + |Extension(env): Extension, Extension(data): Extension, req: axum::extract::Request| async { + let resp = $name(req.try_into().expect("convert request"), env, data).expect("handler result"); + Into::>::into(resp) + } + }; +); +#[cfg(not(feature = "http"))] +macro_rules! handler ( + ($name:path, sync) => { + |req: Request, ctx: RouteContext| { + $name(req, ctx.env, ctx.data) + } + }; + ($name:path) => { + |req: Request, ctx: RouteContext| async { + $name(req, ctx.env, ctx.data).await + } + }; +); + +// Add a route to the router. (Ideally this would be a postfix macro +// https://github.com/rust-lang/rfcs/pull/2442.) +#[cfg(feature = "http")] +macro_rules! add_route ( + ($obj:ident, $method:ident, sync, $route:expr, $name:path) => { + let $obj = $obj.route($route, $method(handler!($name, sync))); + }; + ($obj:ident, $method:ident, $route:expr, $name:path) => { + let $obj = $obj.route($route, $method(handler!($name))); + }; +); +// Use `paste::item` to create an identifier '_async' since that's +// the format for worker::Router's async methods. +#[cfg(not(feature = "http"))] +macro_rules! add_route ( + ($obj:ident, $method:ident, $route:expr, $name:path) => { + paste::item! { + let $obj = $obj.[<$method _ async>]($route, handler!($name)); + } + }; + ($obj:ident, $method:ident, sync, $route:expr, $name:path) => { + let $obj = $obj.$method($route, handler!($name, sync)); + }; +); + +macro_rules! add_routes ( + ($obj:ident) => { + add_route!($obj, get, sync, "/request", request::handle_a_request); + add_route!($obj, get, "/analytics-engine", analytics_engine::handle_analytics_event); + add_route!($obj, get, "/async-request", request::handle_async_request); + add_route!($obj, get, format_route!("/asset/{}", "name"), assets::handle_asset); + add_route!($obj, get, "/websocket", ws::handle_websocket); + add_route!($obj, get, "/got-close-event", handle_close_event); + add_route!($obj, get, "/ws-client",ws::handle_websocket_client); + add_route!($obj, get, "/test-data", request::handle_test_data); + add_route!($obj, post, format_route!("/xor/{}", "num"), request::handle_xor); + add_route!($obj, post, "/headers", request::handle_headers); + add_route!($obj, post, "/formdata-name", form::handle_formdata_name); + add_route!($obj, post, "/is-secret", form::handle_is_secret); + add_route!($obj, post, "/formdata-file-size", form::handle_formdata_file_size); + add_route!($obj, get, format_route!("/formdata-file-size/{}", "hash"), form::handle_formdata_file_size_hash); + add_route!($obj, post, "/post-file-size",request::handle_post_file_size); + add_route!($obj, get, format_route!("/user/{}/test", "id"), user::handle_user_id_test); + add_route!($obj, get, format_route!("/user/{}", "id"), user::handle_user_id); + add_route!($obj, post, format_route!("/account/{}/zones", "id"), user::handle_post_account_id_zones); + add_route!($obj, get, format_route!("/account/{}/zones","id"), user::handle_get_account_id_zones); + add_route!($obj, post, "/async-text-echo", request::handle_async_text_echo); + add_route!($obj, get, "/fetch",fetch::handle_fetch); + add_route!($obj, get, "/fetch_json",fetch::handle_fetch_json); + add_route!($obj, get, format_route!("/proxy_request/{}", "*url") ,fetch::handle_proxy_request); + add_route!($obj, get, "/durable/alarm", alarm::handle_alarm); + add_route!($obj, get, format_route!("/durable/{}", "id"), counter::handle_id); + add_route!($obj, get, "/durable/put-raw", put_raw::handle_put_raw); + add_route!($obj, get, "/durable/websocket", counter::handle_websocket); + add_route!($obj, get, "/secret", request::handle_secret); + add_route!($obj, get, "/var", request::handle_var); + add_route!($obj, get, "/object-var", request::handle_object_var); + add_route!($obj, post, format_route!("/kv/{}/{}", "key", "value"), kv::handle_post_key_value); + add_route!($obj, get, "/bytes",request::handle_bytes); + add_route!($obj, post, "/api-data",request::handle_api_data); + add_route!($obj, post, "/nonsense-repeat", request::handle_nonsense_repeat); + add_route!($obj, get, format_route!("/status/{}", "code"), request::handle_status); + add_route!($obj, put, sync, "/",respond); + add_route!($obj, patch, sync, "/", respond); + add_route!($obj, delete, sync, "/",respond); + add_route!($obj, head, sync, "/", respond); + add_route!($obj, put, "/async", respond_async); + add_route!($obj, patch, "/async", respond_async); + add_route!($obj, delete, "/async", respond_async); + add_route!($obj, head, "/async", respond_async); + add_route!($obj, options, format_route!("/{}", "*catchall"), handle_options_catchall); + add_route!($obj, get, "/request-init-fetch", fetch::handle_request_init_fetch); + add_route!($obj, get, "/request-init-fetch-post", fetch::handle_request_init_fetch_post); + add_route!($obj, get, "/cancelled-fetch", fetch::handle_cancelled_fetch); + add_route!($obj, get, "/fetch-timeout", fetch::handle_fetch_timeout); + add_route!($obj, get, "/redirect-default", request::handle_redirect_default); + add_route!($obj, get, "/redirect-307", request::handle_redirect_307); + add_route!($obj, get, "/now", request::handle_now); + add_route!($obj, get, "/cloned", request::handle_cloned); + add_route!($obj, get, "/cloned-stream", request::handle_cloned_stream); + add_route!($obj, get, "/cloned-fetch", fetch::handle_cloned_fetch); + add_route!($obj, get, "/cloned-response", fetch::handle_cloned_response_attributes); + add_route!($obj, get, format_route!("/wait/{}", "delay"), request::handle_wait_delay); + add_route!($obj, get, "/custom-response-body", request::handle_custom_response_body); + add_route!($obj, get, "/init-called", handle_init_called); + add_route!($obj, get, "/cache-example", cache::handle_cache_example); + add_route!($obj, get, format_route!("/cache-api/get/{}", "key"), cache::handle_cache_api_get); + add_route!($obj, put, format_route!("/cache-api/put/{}", "key"), cache::handle_cache_api_put); + add_route!($obj, post, format_route!("/cache-api/delete/{}", "key"), cache::handle_cache_api_delete); + add_route!($obj, get, "/cache-stream", cache::handle_cache_stream); + add_route!($obj, get, "/remote-by-request", service::handle_remote_by_request); + add_route!($obj, get, "/remote-by-path", service::handle_remote_by_path); + add_route!($obj, post, format_route!("/queue/send/{}", "id"), queue::handle_queue_send); + add_route!($obj, post, "/queue/send_batch", queue::handle_batch_send); + add_route!($obj, get, "/queue",queue::handle_queue); + add_route!($obj, get, "/d1/prepared", d1::prepared_statement); + add_route!($obj, get, "/d1/batch", d1::batch); + add_route!($obj, get, "/d1/dump", d1::dump); + add_route!($obj, post, "/d1/exec", d1::exec); + add_route!($obj, get, "/d1/error", d1::error); + add_route!($obj, get, "/d1/jsvalue_null_is_null", d1::jsvalue_null_is_null); + add_route!($obj, get, "/d1/serialize_optional_none", d1::serialize_optional_none); + add_route!($obj, get, "/d1/serialize_optional_some", d1::serialize_optional_some); + add_route!($obj, get, "/d1/deserialize_optional_none", d1::deserialize_optional_none); + add_route!($obj, get, "/d1/insert_and_retrieve_optional_none", d1::insert_and_retrieve_optional_none); + add_route!($obj, get, "/d1/insert_and_retrieve_optional_some", d1::insert_and_retrieve_optional_some); + add_route!($obj, get, "/d1/retrieve_optional_none", d1::retrieve_optional_none); + add_route!($obj, get, "/d1/retrieve_optional_some", d1::retrieve_optional_some); + add_route!($obj, get, "/d1/retrive_first_none", d1::retrive_first_none); + add_route!($obj, get, "/kv/get", kv::get); + add_route!($obj, get, "/kv/get-not-found", kv::get_not_found); + add_route!($obj, get, "/kv/list-keys", kv::list_keys); + add_route!($obj, get, "/kv/put-simple", kv::put_simple); + add_route!($obj, get, "/kv/put-metadata", kv::put_metadata); + add_route!($obj, get, "/kv/put-metadata-struct", kv::put_metadata_struct); + add_route!($obj, get, "/kv/put-expiration", kv::put_expiration); + add_route!($obj, get, "/r2/list-empty", r2::list_empty); + add_route!($obj, get, "/r2/list", r2::list); + add_route!($obj, get,"/r2/get-empty", r2::get_empty); + add_route!($obj, get, "/r2/get", r2::get); + add_route!($obj, put, "/r2/put", r2::put); + add_route!($obj, put, "/r2/put-properties", r2::put_properties); + add_route!($obj, put, "/r2/put-multipart", r2::put_multipart); + add_route!($obj, delete, "/r2/delete", r2::delete); + add_route!($obj, get, "/socket/failed", socket::handle_socket_failed); + add_route!($obj, get, "/socket/read", socket::handle_socket_read); + add_route!($obj, get, "/durable/auto-response", auto_response::handle_auto_response); + add_route!($obj, get, "/durable/hello", durable::handle_hello); + add_route!($obj, get, "/durable/hello-unique", durable::handle_hello_unique); + add_route!($obj, get, "/durable/storage", durable::handle_storage); + add_route!($obj, get, "/durable/handle-basic-test", durable::handle_basic_test); + add_route!($obj, get, "/durable/get-by-name", durable::handle_get_by_name); + add_route!($obj, get, "/durable/get-by-name-with-location-hint", durable::handle_get_by_name_with_location_hint); + add_route!($obj, get, "/js_snippets/now", js_snippets::performance_now); + add_route!($obj, get, "/js_snippets/log", js_snippets::console_log); + add_route!($obj, get, format_route!("/sql-counter/{}", "*path"), sql_counter::handle_sql_counter); + add_route!($obj, get, format_route!("/sql-iterator/{}", "*path"), sql_iterator::handle_sql_iterator); + add_route!($obj, get, "/get-from-secret-store", secret_store::get_from_secret_store); + add_route!($obj, get, "/get-from-secret-store-missing", secret_store::get_from_secret_store_missing); + add_route!($obj, get, sync, "/test-panic", handle_test_panic); + add_route!($obj, get, sync, "/test-abort", handle_test_abort); + add_route!($obj, get, sync, "/test-oom", handle_test_oom); + add_route!($obj, get, sync, "/test-js-error", js_snippets::throw_js_error); + add_route!($obj, post, "/container/echo", container::handle_container); + add_route!($obj, get, "/container/ws", container::handle_container); + add_route!($obj, get, "/rate-limit/check", rate_limit::handle_rate_limit_check); + add_route!($obj, get, format_route!("/rate-limit/key/{}", "key"), rate_limit::handle_rate_limit_with_key); + add_route!($obj, get, "/rate-limit/bulk-test", rate_limit::handle_rate_limit_bulk_test); + add_route!($obj, get, "/rate-limit/reset", rate_limit::handle_rate_limit_reset); +}); + +#[cfg(feature = "http")] +pub fn make_router(data: SomeSharedData, env: Env) -> axum::Router { + let router = axum::Router::new(); + add_routes!(router); + router + .fallback(get(handler!(catchall))) + .layer(Extension(env)) + .layer(Extension(data)) +} + +#[cfg(not(feature = "http"))] +pub fn make_router<'a>(data: SomeSharedData) -> Router<'a, SomeSharedData> { + let router = Router::with_data(data); + add_routes!(router); + router.or_else_any_method_async("/*catchall", handler!(catchall)) +} + +#[allow(clippy::needless_pass_by_value)] +fn respond(req: Request, _env: Env, _data: SomeSharedData) -> Result { + ResponseBuilder::new() + .with_header("x-testing", "123")? + .ok(format!("Ok: {}", String::from(req.method()))) +} + +async fn respond_async(req: Request, _env: Env, _data: SomeSharedData) -> Result { + ResponseBuilder::new() + .with_header("x-testing", "123")? + .ok(format!("Ok (async): {}", String::from(req.method()))) +} + +#[worker::send] +async fn handle_close_event(_req: Request, env: Env, _data: SomeSharedData) -> Result { + let some_namespace_kv = env.kv("SOME_NAMESPACE")?; + let got_close_event = some_namespace_kv + .get("got-close-event") + .text() + .await? + .unwrap_or_else(|| "false".into()); + + // Let the integration tests have some way of knowing if we successfully received the closed event. + Response::ok(got_close_event) +} + +#[worker::send] +async fn catchall(req: Request, _env: Env, _data: SomeSharedData) -> Result { + let uri = req.url()?; + let path = uri.path(); + console_log!("[or_else_any_method_async] caught: {}", path); + + let (builder, body) = Fetch::Url("https://github.com/404".parse().unwrap()) + .send() + .await? + .into_parts(); + + Ok(builder.with_status(404).body(body)) +} + +async fn handle_options_catchall( + req: Request, + _env: Env, + _data: SomeSharedData, +) -> Result { + let uri = req.url()?; + let path = uri.path(); + Response::ok(path) +} + +async fn handle_init_called(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + let init_called = GLOBAL_STATE.load(Ordering::SeqCst); + Response::ok(init_called.to_string()) +} + +fn handle_test_panic(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + panic!("Intentional panic for testing context abort functionality"); +} + +fn handle_test_abort(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + // Explicitly call abort() to verify raw aborts are also recoverable + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + + #[cfg(not(target_arch = "wasm32"))] + std::process::abort(); +} + +fn handle_test_oom(_req: Request, _env: Env, _data: SomeSharedData) -> Result { + // Attempt to allocate excessive memory to trigger OOM + let mut vecs = Vec::new(); + loop { + vecs.push(vec![0u8; 1024 * 1024 * 1024]); // 1GB chunks + } +} diff --git a/test/src/secret_store.rs b/test/src/secret_store.rs new file mode 100644 index 00000000..9410356a --- /dev/null +++ b/test/src/secret_store.rs @@ -0,0 +1,32 @@ +use crate::SomeSharedData; +use worker::{Env, Request, Response, Result}; + +#[worker::send] +pub async fn get_from_secret_store( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let secrets = env.secret_store("SECRETS")?; + let secret_value = secrets.get().await?; + + match secret_value { + Some(_value) => Response::ok("secret value"), + None => Response::error("Secret not found", 404), + } +} + +#[worker::send] +pub async fn get_from_secret_store_missing( + _req: Request, + env: Env, + _data: SomeSharedData, +) -> Result { + let secrets = env.secret_store("MISSING_SECRET")?; + let secret_value = secrets.get().await?; + + match secret_value { + Some(value) => Response::ok(value), + None => Response::error("Secret not found", 500), + } +} diff --git a/worker-sandbox/src/service.rs b/test/src/service.rs similarity index 83% rename from worker-sandbox/src/service.rs rename to test/src/service.rs index 15c83f69..91fc2bcd 100644 --- a/worker-sandbox/src/service.rs +++ b/test/src/service.rs @@ -11,12 +11,7 @@ pub async fn handle_remote_by_request( ) -> Result { let fetcher = env.service("remote")?; - #[cfg(feature = "http")] - let http_request = req.try_into()?; - #[cfg(not(feature = "http"))] - let http_request = req; - - let response = fetcher.fetch_request(http_request).await?; + let response = fetcher.fetch_request(req).await?; #[cfg(feature = "http")] let result = Ok(TryInto::::try_into(response)?); diff --git a/worker-sandbox/src/socket.rs b/test/src/socket.rs similarity index 95% rename from worker-sandbox/src/socket.rs rename to test/src/socket.rs index c6888a01..81fe2983 100644 --- a/worker-sandbox/src/socket.rs +++ b/test/src/socket.rs @@ -17,7 +17,7 @@ pub async fn handle_socket_failed( "Socket should have failed to open.".to_owned(), )) } - Err(e) => Response::ok(format!("{:?}", e)), + Err(e) => Response::ok(format!("{e:?}")), } } diff --git a/test/src/sql_counter.rs b/test/src/sql_counter.rs new file mode 100644 index 00000000..93d92395 --- /dev/null +++ b/test/src/sql_counter.rs @@ -0,0 +1,111 @@ +use worker::{durable_object, wasm_bindgen, Env, Request, Response, Result, SqlStorage, State}; + +/// A simple SQLite-backed counter stored in Durable Object storage. +/// +/// Each Durable Object instance owns its own private SQLite database. We keep a +/// single table `counter` with one row that stores the current value. +#[durable_object] +pub struct SqlCounter { + sql: SqlStorage, +} + +impl DurableObject for SqlCounter { + fn new(state: State, _env: Env) -> Self { + let sql = state.storage().sql(); + // Create table if it does not exist. Note: `exec` is synchronous. + sql.exec("CREATE TABLE IF NOT EXISTS counter(value INTEGER);", None) + .expect("create table"); + Self { sql } + } + + async fn fetch(&self, req: Request) -> Result { + let url = req.url()?; + let path = url.path(); + + // Parse path to determine action + if path.contains("/set-large/") { + self.handle_set_large_value(&url) + } else { + self.handle_increment() + } + } +} + +impl SqlCounter { + fn handle_increment(&self) -> Result { + // Read current value (if any) + #[derive(serde::Deserialize)] + struct Row { + value: i32, + } + + let rows: Vec = self + .sql + .exec("SELECT value FROM counter LIMIT 1;", None)? + .to_array()?; + let current = rows.first().map_or(0, |r| r.value); + let next = current + 1; + + // Upsert new value – simplest way: delete and insert again. + self.sql.exec("DELETE FROM counter;", None)?; + self.sql + .exec("INSERT INTO counter(value) VALUES (?);", vec![next.into()])?; + + Response::ok(format!("SQL counter is now {next}")) + } + + fn handle_set_large_value(&self, url: &worker::Url) -> Result { + // Extract the large value from the path /set-large/{value} + let path = url.path(); + let value_str = path.split("/set-large/").nth(1).unwrap_or("0"); + + // Parse the value as i64 + let large_value: i64 = value_str + .parse() + .map_err(|_| worker::Error::from("Invalid number format"))?; + + // Use try_from_i64 to safely create SqlStorageValue + let safe_value = match worker::SqlStorageValue::try_from_i64(large_value) { + Ok(value) => value, + Err(e) => { + return Response::ok(format!( + "Error: Cannot store value {large_value} - {e}. JavaScript safe range is ±9007199254740991" + )); + } + }; + + // Store the safe value + self.sql.exec("DELETE FROM counter;", None)?; + self.sql + .exec("INSERT INTO counter(value) VALUES (?);", vec![safe_value])?; + + Response::ok(format!("Successfully stored large value: {large_value}")) + } +} + +#[worker::send] +/// Route handler that proxies a request to our SqlCounter Durable Object with id derived from the +/// path `/sql-counter/{name}` (so every name gets its own instance). +pub async fn handle_sql_counter( + req: Request, + env: Env, + _data: super::SomeSharedData, +) -> Result { + let uri = req.url()?; + let mut segments = uri.path_segments().unwrap(); + // skip "sql-counter" + let _ = segments.next(); + let name = segments.next().unwrap_or("default"); + + // Build the remaining path for the durable object request + let remaining_path: Vec<&str> = segments.collect(); + let full_path = if remaining_path.is_empty() { + "https://fake-host/".to_string() + } else { + format!("https://fake-host/%7B%7D", remaining_path.join("/")) + }; + + let namespace = env.durable_object("SQL_COUNTER")?; + let stub = namespace.id_from_name(name)?.get_stub()?; + stub.fetch_with_str(&full_path).await +} diff --git a/test/src/sql_iterator.rs b/test/src/sql_iterator.rs new file mode 100644 index 00000000..014c39f2 --- /dev/null +++ b/test/src/sql_iterator.rs @@ -0,0 +1,443 @@ +use serde::Deserialize; +use worker::{ + durable_object, wasm_bindgen, Env, Request, Response, Result, SqlStorage, SqlStorageValue, + State, +}; + +/// A Durable Object that demonstrates SQL cursor iterator methods. +/// +/// This example creates a table with sample data and provides endpoints +/// to test both the next() and raw() iterator methods. +#[durable_object] +pub struct SqlIterator { + sql: SqlStorage, +} + +#[derive(Deserialize)] +struct Product { + id: i32, + name: String, + price: f64, + in_stock: i32, +} + +#[derive(Deserialize)] +struct BadProduct { + id: String, // This will cause deserialization to fail since id is actually an integer + name: String, + price: f64, + in_stock: i32, +} + +#[derive(Debug)] +struct BlobData { + id: i32, + name: String, + data: Vec, +} + +impl BlobData { + fn from_raw_row(row: &[SqlStorageValue]) -> Option { + if row.len() != 3 { + return None; + } + + let id = match &row[0] { + SqlStorageValue::Integer(i) => *i as i32, + _ => return None, + }; + + let name = match &row[1] { + SqlStorageValue::String(s) => s.clone(), + _ => return None, + }; + + let data = match &row[2] { + SqlStorageValue::Blob(bytes) => bytes.clone(), + _ => return None, + }; + + Some(BlobData { id, name, data }) + } +} + +impl DurableObject for SqlIterator { + fn new(state: State, _env: Env) -> Self { + let sql = state.storage().sql(); + + // Create table and seed with test data + sql.exec( + "CREATE TABLE IF NOT EXISTS products(id INTEGER PRIMARY KEY, name TEXT, price REAL, in_stock INTEGER);", + None, + ).expect("create table"); + + sql.exec( + "CREATE TABLE IF NOT EXISTS blob_data(id INTEGER PRIMARY KEY, name TEXT, data BLOB);", + None, + ) + .expect("create blob table"); + + // Check if we need to seed data + let count: Vec = sql + .exec("SELECT COUNT(*) as count FROM products;", None) + .expect("count query") + .to_array() + .expect("count result"); + + if count + .first() + .and_then(|v| v.get("count")) + .and_then(serde_json::Value::as_i64) + .unwrap_or(0) + == 0 + { + // Seed with test data + let products = vec![ + ("Laptop", 999.99, true), + ("Mouse", 29.99, true), + ("Keyboard", 79.99, false), + ("Monitor", 299.99, true), + ("Headphones", 149.99, false), + ]; + + for (name, price, in_stock) in products { + sql.exec( + "INSERT INTO products(name, price, in_stock) VALUES (?, ?, ?);", + vec![name.into(), price.into(), i32::from(in_stock).into()], + ) + .expect("insert product"); + } + } + + let blob_count: Vec = sql + .exec("SELECT COUNT(*) as count FROM blob_data;", None) + .expect("blob count query") + .to_array() + .expect("blob count result"); + + if blob_count + .first() + .and_then(|v| v.get("count")) + .and_then(serde_json::Value::as_i64) + .unwrap_or(0) + == 0 + { + let blob_test_data = vec![ + ("binary_data", vec![0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE]), + ("empty_blob", vec![]), + ("text_as_blob", "Hello, World!".as_bytes().to_vec()), + ( + "large_blob", + (0u8..=255).cycle().take(1000).collect::>(), + ), + ]; + + for (name, data) in blob_test_data { + sql.exec( + "INSERT INTO blob_data(name, data) VALUES (?, ?);", + vec![name.into(), SqlStorageValue::Blob(data)], + ) + .expect("insert blob data"); + } + } + + Self { sql } + } + + async fn fetch(&self, req: Request) -> Result { + let url = req.url()?; + let path = url.path(); + + match path { + "/next" => self.handle_next(), + "/raw" => self.handle_raw(), + "/next-invalid" => self.handle_next_invalid(), + "/blob-next" => self.handle_blob_next(), + "/blob-raw" => self.handle_blob_raw(), + "/blob-roundtrip" => self.handle_blob_roundtrip(), + _ => Response::ok("SQL Iterator Test - try /next, /raw, /next-invalid, /blob-next, /blob-raw, or /blob-roundtrip endpoints"), + } + } +} + +impl SqlIterator { + fn handle_next(&self) -> Result { + let cursor = self.sql.exec("SELECT * FROM products ORDER BY id;", None)?; + + let mut results = Vec::new(); + let iterator = cursor.next::(); + + for result in iterator { + match result { + Ok(product) => { + results.push(format!( + "Product {}: {} - ${:.2} (in stock: {})", + product.id, + product.name, + product.price, + product.in_stock != 0 + )); + } + Err(e) => { + results.push(format!("Error deserializing row: {e}")); + } + } + } + + let response_body = format!("next() iterator results:\n{}", results.join("\n")); + + Response::ok(response_body) + } + + fn handle_raw(&self) -> Result { + let cursor = self.sql.exec("SELECT * FROM products ORDER BY id;", None)?; + + let mut results = Vec::new(); + let column_names = cursor.column_names(); + results.push(format!("Columns: {}", column_names.join(", "))); + + let iterator = cursor.raw(); + + for result in iterator { + match result { + Ok(row) => { + let row_str: Vec = row.iter().map(|v| format!("{v:?}")).collect(); + results.push(format!("Row: [{}]", row_str.join(", "))); + } + Err(e) => { + results.push(format!("Error reading row: {e}")); + } + } + } + + let response_body = format!("raw() iterator results:\n{}", results.join("\n")); + + Response::ok(response_body) + } + + fn handle_next_invalid(&self) -> Result { + let cursor = self.sql.exec("SELECT * FROM products ORDER BY id;", None)?; + + let mut results = Vec::new(); + let iterator = cursor.next::(); + + for result in iterator { + match result { + Ok(product) => { + results.push(format!( + "BadProduct {}: {} - ${:.2} (in stock: {})", + product.id, + product.name, + product.price, + product.in_stock != 0 + )); + } + Err(e) => { + results.push(format!("Error deserializing row: {e}")); + } + } + } + + let response_body = format!("next-invalid() iterator results:\n{}", results.join("\n")); + + Response::ok(response_body) + } + + fn handle_blob_next(&self) -> Result { + let cursor = self + .sql + .exec("SELECT * FROM blob_data ORDER BY id;", None)?; + + let mut results = Vec::new(); + let iterator = cursor.raw(); + + for result in iterator { + match result { + Ok(row) => { + if let Some(blob_data) = BlobData::from_raw_row(&row) { + let data_preview = if blob_data.data.len() <= 10 { + format!("{:?}", blob_data.data) + } else { + format!( + "{:?}...[{} bytes total]", + &blob_data.data[..10], + blob_data.data.len() + ) + }; + + results.push(format!( + "BlobData {}: {} - data: {}", + blob_data.id, blob_data.name, data_preview + )); + } else { + results.push("Error: Failed to parse blob row".to_string()); + } + } + Err(e) => { + results.push(format!("Error reading blob row: {e}")); + } + } + } + + let response_body = format!("blob-next() iterator results:\n{}", results.join("\n")); + + Response::ok(response_body) + } + + fn handle_blob_raw(&self) -> Result { + let cursor = self + .sql + .exec("SELECT * FROM blob_data ORDER BY id;", None)?; + + let mut results = Vec::new(); + let column_names = cursor.column_names(); + results.push(format!("Columns: {}", column_names.join(", "))); + + let iterator = cursor.raw(); + + for result in iterator { + match result { + Ok(row) => { + let mut row_str = Vec::new(); + for (i, value) in row.iter().enumerate() { + if i == 2 { + // 'data' column is index 2 (id=0, name=1, data=2) + match value { + SqlStorageValue::Blob(bytes) => { + if bytes.len() <= 10 { + row_str.push(format!("Blob({:?})", bytes)); + } else { + row_str.push(format!( + "Blob({:?}...[{} bytes])", + &bytes[..10], + bytes.len() + )); + } + } + _ => row_str.push(format!("{:?}", value)), + } + } else { + row_str.push(format!("{:?}", value)); + } + } + results.push(format!("Row: [{}]", row_str.join(", "))); + } + Err(e) => { + results.push(format!("Error reading blob row: {e}")); + } + } + } + + let response_body = format!("blob-raw() iterator results:\n{}", results.join("\n")); + + Response::ok(response_body) + } + + fn handle_blob_roundtrip(&self) -> Result { + // Test data roundtrip: insert a BLOB and immediately read it back + let test_data = vec![0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF]; + let test_name = "roundtrip_test"; + + // Insert test data + self.sql.exec( + "INSERT INTO blob_data(name, data) VALUES (?, ?);", + vec![test_name.into(), SqlStorageValue::Blob(test_data.clone())], + )?; + + // Read it back using both methods (raw iterator approach for both) + let cursor_next = self.sql.exec( + "SELECT * FROM blob_data WHERE name = ? ORDER BY id DESC LIMIT 1;", + vec![test_name.into()], + )?; + + let cursor_raw = self.sql.exec( + "SELECT * FROM blob_data WHERE name = ? ORDER BY id DESC LIMIT 1;", + vec![test_name.into()], + )?; + + let mut results = Vec::new(); + results.push(format!("Original data: {:?}", test_data)); + + // Test "next()" style result by converting raw data to BlobData struct + let next_raw_iterator = cursor_next.raw(); + for result in next_raw_iterator { + match result { + Ok(row) => { + if let Some(blob_data) = BlobData::from_raw_row(&row) { + let matches = blob_data.data == test_data; + results.push(format!( + "next() result: {:?}, matches_original: {}", + blob_data.data, matches + )); + } else { + results.push("next() error: Failed to parse blob row".to_string()); + } + } + Err(e) => { + results.push(format!("next() error: {e}")); + } + } + } + + // Test raw iterator + let raw_iterator = cursor_raw.raw(); + for result in raw_iterator { + match result { + Ok(row) => { + if let Some(SqlStorageValue::Blob(data)) = row.get(2) { + let matches = data == &test_data; + results.push(format!( + "raw() result: {:?}, matches_original: {}", + data, matches + )); + } else { + results.push("raw() error: data column is not a blob".to_string()); + } + } + Err(e) => { + results.push(format!("raw() error: {e}")); + } + } + } + + // Clean up test data + self.sql.exec( + "DELETE FROM blob_data WHERE name = ?;", + vec![test_name.into()], + )?; + + let response_body = format!("blob-roundtrip test results:\n{}", results.join("\n")); + + Response::ok(response_body) + } +} + +#[worker::send] +/// Route handler for the SQL iterator test Durable Object. +pub async fn handle_sql_iterator( + req: Request, + env: Env, + _data: super::SomeSharedData, +) -> Result { + let uri = req.url()?; + let mut segments = uri.path_segments().unwrap(); + // skip "sql-iterator" + let _ = segments.next(); + + // Get name and remaining path + let name = segments.next().unwrap_or("default"); + let remaining_path: Vec<&str> = segments.collect(); + let path = if remaining_path.is_empty() { + "/" + } else { + &format!("/{}", remaining_path.join("/")) + }; + + let namespace = env.durable_object("SQL_ITERATOR")?; + let stub = namespace.id_from_name(name)?.get_stub()?; + + // Forward the request path to the DO + let new_url = format!("https://fake-host{path}/"); + + stub.fetch_with_str(&new_url).await +} diff --git a/worker-sandbox/src/user.rs b/test/src/user.rs similarity index 95% rename from worker-sandbox/src/user.rs rename to test/src/user.rs index 6f5e2e69..d8149b74 100644 --- a/worker-sandbox/src/user.rs +++ b/test/src/user.rs @@ -32,7 +32,7 @@ pub async fn handle_user_id(req: Request, _env: Env, _data: SomeSharedData) -> R return Response::from_json(&User { id: id.to_string(), timestamp: Date::now().as_millis(), - date_from_int: Date::new(DateInit::Millis(1234567890)).to_string(), + date_from_int: Date::new(DateInit::Millis(1_234_567_890)).to_string(), date_from_str: Date::new(DateInit::String( "Wed Jan 14 1980 23:56:07 GMT-0700 (Mountain Standard Time)".into(), )) diff --git a/worker-sandbox/src/ws.rs b/test/src/ws.rs similarity index 100% rename from worker-sandbox/src/ws.rs rename to test/src/ws.rs diff --git a/test/tests/analytics_engine.spec.ts b/test/tests/analytics_engine.spec.ts new file mode 100644 index 00000000..abc24388 --- /dev/null +++ b/test/tests/analytics_engine.spec.ts @@ -0,0 +1,12 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("analytics engine", () => { + test("write data point", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}analytics-engine`, + ); + console.log(await resp.text()) + expect(resp.status).toBe(200); + }); +}); diff --git a/test/tests/assets.spec.ts b/test/tests/assets.spec.ts new file mode 100644 index 00000000..197fb35d --- /dev/null +++ b/test/tests/assets.spec.ts @@ -0,0 +1,10 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("assets", () => { + test("assets example", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}asset/test.txt`); + const body = await resp.text(); + expect(body).toBe("TEST"); + }); +}); diff --git a/test/tests/auto_response.spec.ts b/test/tests/auto_response.spec.ts new file mode 100644 index 00000000..5db04c80 --- /dev/null +++ b/test/tests/auto_response.spec.ts @@ -0,0 +1,10 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("durable object websocket auto-response", () => { + test("set and get auto-response pair", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}durable/auto-response`); + const text = await resp.text(); + expect(text).toBe("ping:pong"); + }); +}); diff --git a/worker-sandbox/tests/cache.spec.ts b/test/tests/cache.spec.ts similarity index 75% rename from worker-sandbox/tests/cache.spec.ts rename to test/tests/cache.spec.ts index 8301459d..53e9b480 100644 --- a/worker-sandbox/tests/cache.spec.ts +++ b/test/tests/cache.spec.ts @@ -1,13 +1,13 @@ import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; describe("cache", () => { test("cache example", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cache-example"); + const resp = await mf.dispatchFetch(`${mfUrl}cache-example`); const { timestamp } = (await resp.json()) as { timestamp: unknown }; for (let i = 0; i < 5; i++) { - const resp = await mf.dispatchFetch("https://fake.host/cache-example"); + const resp = await mf.dispatchFetch(`${mfUrl}cache-example`); const data = (await resp.json()) as { timestamp: unknown }; expect(data.timestamp).toBe(timestamp); @@ -15,11 +15,11 @@ describe("cache", () => { }); test("cache stream", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cache-stream"); + const resp = await mf.dispatchFetch(`${mfUrl}cache-stream`); const body = await resp.text(); for (let i = 0; i < 5; i++) { - const resp = await mf.dispatchFetch("https://fake.host/cache-stream"); + const resp = await mf.dispatchFetch(`${mfUrl}cache-stream`); const cachedBody = await resp.text(); expect(cachedBody).toBe(body); @@ -28,9 +28,9 @@ describe("cache", () => { test("cache api", async () => { const key = "example.org"; - const getEndpoint = `https://fake.host/cache-api/get/${key}`; - const putEndpoint = `https://fake.host/cache-api/put/${key}`; - const deleteEndpoint = `https://fake.host/cache-api/delete/${key}`; + const getEndpoint = `${mfUrl}cache-api/get/${key}`; + const putEndpoint = `${mfUrl}cache-api/put/${key}`; + const deleteEndpoint = `${mfUrl}cache-api/delete/${key}`; // First time should result in cache miss let resp = await mf.dispatchFetch(getEndpoint); diff --git a/worker-sandbox/tests/clone.spec.ts b/test/tests/clone.spec.ts similarity index 56% rename from worker-sandbox/tests/clone.spec.ts rename to test/tests/clone.spec.ts index a7313812..1fcbb37f 100644 --- a/worker-sandbox/tests/clone.spec.ts +++ b/test/tests/clone.spec.ts @@ -1,24 +1,24 @@ import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; describe("cache", () => { test("cloned", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cloned"); + const resp = await mf.dispatchFetch(`${mfUrl}cloned`); expect(await resp.text()).toBe("true"); }); test("cloned stream", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cloned-stream"); + const resp = await mf.dispatchFetch(`${mfUrl}cloned-stream`); expect(await resp.text()).toBe("true"); }); test("cloned fetch", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cloned-fetch"); + const resp = await mf.dispatchFetch(`${mfUrl}cloned-fetch`); expect(await resp.text()).toBe("true"); }); test("cloned response", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cloned-response"); + const resp = await mf.dispatchFetch(`${mfUrl}cloned-response`); expect(await resp.text()).toBe("true"); }); }); diff --git a/test/tests/container.spec.ts b/test/tests/container.spec.ts new file mode 100644 index 00000000..dcadde7e --- /dev/null +++ b/test/tests/container.spec.ts @@ -0,0 +1,47 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; +import { MessageEvent } from "miniflare"; + +describe("container", () => { + let testContainer = true; + if (!process.env.TEST_CONTAINER_NAME) { + console.log('No container specified, skipping container test'); + testContainer = false; + } + + (testContainer ? test : test.skip)("post-echo", async () => { + const test_text = "Hello container!"; + const resp = await mf.dispatchFetch(`${mfUrl}container/echo`, { + method: "POST", + body: test_text, + }); + expect(await resp.text()).toBe(test_text); + }); + + (testContainer ? test : test.skip)("websocket-to-container", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}container/ws`, { + headers: { + upgrade: "websocket", + }, + }); + expect(resp.webSocket).not.toBeNull(); + + const socket = resp.webSocket!; + socket.accept(); + + const messages = ["123", "223", "323", "abc"]; + + let idx = 0; + socket.addEventListener("message", function (event: MessageEvent) { + expect(event.data).toBe(messages[idx]); + idx++; + }); + + for (const msg of messages) { + socket.send(msg); + await new Promise((resolve) => setTimeout(resolve, 500)); + } + + socket.close(); + }); +}); diff --git a/test/tests/d1.spec.ts b/test/tests/d1.spec.ts new file mode 100644 index 00000000..817dab02 --- /dev/null +++ b/test/tests/d1.spec.ts @@ -0,0 +1,134 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +async function exec(query: string): Promise { + const resp = await mf.dispatchFetch(`${mfUrl}d1/exec`, { + method: "POST", + body: query.split("\n").join(""), + }); + + const body = await resp.text(); + expect(resp.status).toBe(200); + return Number(body); +} + +describe("d1", () => { + test("create table", async () => { + const query = `CREATE TABLE IF NOT EXISTS uniqueTable ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + age INTEGER NOT NULL + );`; + + expect(await exec(query)).toBe(1); + }); + + test("insert data", async () => { + let query = `CREATE TABLE IF NOT EXISTS people ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + age INTEGER NOT NULL + );`; + + expect(await exec(query)).toBe(1); + + query = `INSERT OR IGNORE INTO people + (id, name, age) + VALUES + (1, 'Freddie Pearce', 26), + (2, 'Wynne Ogley', 67), + (3, 'Dorian Fischer', 19), + (4, 'John Smith', 92), + (5, 'Magaret Willamson', 54), + (6, 'Ryan Upton', 21);`; + + expect(await exec(query)).toBe(1); + }); + + test("prepared statement", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/prepared`); + expect(resp.status).toBe(200); + }); + + test("batch", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/batch`); + expect(resp.status).toBe(200); + }); + + test("error", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/error`); + expect(resp.status).toBe(200); + }); + + test("create table nullable", async () => { + let query = `CREATE TABLE IF NOT EXISTS nullable_people ( + id INTEGER PRIMARY KEY, + name TEXT, + age INTEGER + );`; + + expect(await exec(query)).toBe(1); + + query = `INSERT OR IGNORE INTO nullable_people + (id, name, age) + VALUES + (1, NULL, NULL), + (2, 'Wynne Ogley', 67)`; + + expect(await exec(query)).toBe(1); + }); + + test("jsvalue_null_is_null", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/jsvalue_null_is_null`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("serialize_optional_none", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/serialize_optional_none`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("serialize_optional_some", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/serialize_optional_some`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("deserialize_optional_none", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/deserialize_optional_none`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("insert_and_retrieve_optional_none", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/insert_and_retrieve_optional_none`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("insert_and_retrieve_optional_some", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/insert_and_retrieve_optional_some`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("retrieve_optional_none", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/retrieve_optional_none`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("retrieve_optional_some", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/retrieve_optional_some`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); + + test("retrive_first_none", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}d1/retrive_first_none`); + expect(await resp.text()).toBe("ok"); + expect(resp.status).toBe(200); + }); +}); diff --git a/test/tests/durable.spec.ts b/test/tests/durable.spec.ts new file mode 100644 index 00000000..1c7b2dc9 --- /dev/null +++ b/test/tests/durable.spec.ts @@ -0,0 +1,60 @@ +import {describe, test, expect} from "vitest"; +import { mf, mfUrl } from "./mf"; +import {MessageEvent} from "miniflare"; + +describe("durable", () => { + test("put-raw", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}durable/put-raw`); + expect(await resp.text()).toBe("ok"); + }); + + test("websocket-to-durable", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}durable/websocket`, { + headers: { + upgrade: "websocket", + }, + }); + expect(resp.webSocket).not.toBeNull(); + + const socket = resp.webSocket!; + socket.accept(); + + let cnt = 0; + socket.addEventListener("message", function (event: MessageEvent) { + cnt++; + expect(event.data).toMatch(/^10|20|30$/); + }); + let calledClose = false; + socket.addEventListener("close", function (event: CloseEvent) { + calledClose = true; + }); + + socket.send("hi, can you ++?"); + await new Promise((resolve) => setTimeout(resolve, 500)); + expect(cnt).toBe(1); + + socket.send("hi again, more ++?"); + await new Promise((resolve) => setTimeout(resolve, 500)); + expect(cnt).toBe(2); + + socket.close(); + + // TODO: Investigate why this is not passing + // await new Promise(resolve => setTimeout(resolve, 1000)); + // expect(calledClose).toBe(true); + }); + + test("get-by-name", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}durable/get-by-name`); + expect(resp.status).toBe(200); + const text = await resp.text(); + expect(text).toBe("Hello from my-durable-object!"); + }); + + test("get-by-name-with-location-hint", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}durable/get-by-name-with-location-hint`); + expect(resp.status).toBe(200); + const text = await resp.text(); + expect(text).toBe("Hello from my-durable-object!"); + }); +}); diff --git a/worker-sandbox/tests/kv.spec.ts b/test/tests/kv.spec.ts similarity index 94% rename from worker-sandbox/tests/kv.spec.ts rename to test/tests/kv.spec.ts index d528ad29..102fa43f 100644 --- a/worker-sandbox/tests/kv.spec.ts +++ b/test/tests/kv.spec.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; const CASES = [ "get", diff --git a/worker-sandbox/tests/mf-socket.ts b/test/tests/mf-socket.ts similarity index 82% rename from worker-sandbox/tests/mf-socket.ts rename to test/tests/mf-socket.ts index c5dd3013..7c00fbb0 100644 --- a/worker-sandbox/tests/mf-socket.ts +++ b/test/tests/mf-socket.ts @@ -10,8 +10,8 @@ export const server = createServer(function (socket) { }).listen(8080); export const mf = new Miniflare({ - scriptPath: "./build/worker/shim.mjs", - compatibilityDate: "2023-05-18", + scriptPath: "./build/index.js", + compatibilityDate: "2024-12-05", modules: true, modulesRules: [ { type: "CompiledWasm", include: ["**/*.wasm"], fallthrough: true }, @@ -20,3 +20,5 @@ export const mf = new Miniflare({ network: { allow: ["local"] } } }); + +export const mfUrl = await mf.ready; diff --git a/test/tests/mf.ts b/test/tests/mf.ts new file mode 100644 index 00000000..7a63c7d3 --- /dev/null +++ b/test/tests/mf.ts @@ -0,0 +1,143 @@ +import { Miniflare, Response } from "miniflare"; +import { MockAgent } from "undici"; + +const mockAgent = new MockAgent(); + +mockAgent + .get("https://cloudflare.com/") + .intercept({ path: "/" }) + .reply(200, "cloudflare!"); + +mockAgent + .get("https://miniflare.mocks/") + .intercept({ path: "/delay" }) + .reply(200, "cloudflare!") + .delay(10000); + +mockAgent + .get("https://jsonplaceholder.typicode.com/") + .intercept({ path: "/todos/1" }) + .reply( + 200, + { + userId: 1, + id: 1, + title: "delectus aut autem", + completed: false, + }, + { + headers: { + "content-type": "application/json", + }, + } + ); + +const mf_instance = new Miniflare({ + d1Persist: false, + kvPersist: false, + r2Persist: false, + cachePersist: false, + workers: [ + { + scriptPath: "./build/index.js", + compatibilityDate: "2025-07-24", + cache: true, + d1Databases: ["DB"], + modules: true, + modulesRules: [ + { type: "CompiledWasm", include: ["**/*.wasm"], fallthrough: true }, + ], + bindings: { + EXAMPLE_SECRET: "example", + SOME_SECRET: "secret!", + SOME_VARIABLE: "some value", + SOME_OBJECT_VARIABLE: { + foo: 42, + bar: "string" + }, + }, + durableObjects: { + COUNTER: "Counter", + PUT_RAW_TEST_OBJECT: "PutRawTestObject", + AUTO: "AutoResponseObject", + MY_CLASS: "MyClass", + ECHO_CONTAINER: { + className: "EchoContainer", + useSQLite: true, + container: { + imageName: "worker-dev/echocontainer:latest", + } + }, + SQL_COUNTER: { + className: "SqlCounter", + useSQLite: true, + }, + SQL_ITERATOR: { + className: "SqlIterator", + useSQLite: true, + }, + }, + kvNamespaces: ["SOME_NAMESPACE", "FILE_SIZES", "TEST"], + serviceBindings: { + async remote() { + return new Response("hello world"); + }, + }, + r2Buckets: ["EMPTY_BUCKET", "PUT_BUCKET", "SEEDED_BUCKET", "DELETE_BUCKET"], + queueConsumers: { + my_queue: { + maxBatchTimeout: 1, + }, + }, + queueProducers: ["my_queue", "my_queue"], + fetchMock: mockAgent, + assets: { + directory: "./public", + binding: "ASSETS", + routerConfig: { + has_user_worker: true + } + }, + secretsStoreSecrets: { + SECRETS: { + store_id: "SECRET_STORE", + secret_name: "secret-name" + }, + MISSING_SECRET: { + store_id: "SECRET_STORE_MISSING", + secret_name: "missing-secret" + } + }, + wrappedBindings: { + HTTP_ANALYTICS: { + scriptName: "mini-analytics-engine" // mock out analytics engine binding to the "mini-analytics-engine" worker + } + }, + ratelimits: { + TEST_RATE_LIMITER: { + simple: { + limit: 10, + period: 60, + } + } + } + }, + { + name: "mini-analytics-engine", + modules: true, + script: `export default function (env) { + return { + writeDataPoint(data) { + console.log(data) + } + } + }` + }] +}); + +// Seed the secret store with a value using the new API +const secretAPI = await mf_instance.getSecretsStoreSecretAPI("SECRETS"); +await secretAPI().create("secret value"); + +export const mf = mf_instance; +export const mfUrl = await mf.ready; diff --git a/test/tests/panic.spec.ts b/test/tests/panic.spec.ts new file mode 100644 index 00000000..2556052c --- /dev/null +++ b/test/tests/panic.spec.ts @@ -0,0 +1,129 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("Panic Hook with WASM Reinitialization", () => { + // These tests are explicitly run sequentially with a longer timeout + // to ensure they fully run the reinitialization lifecycle. + test("panic recovery tests", async () => { + // basic panic recovery + { + await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + expect(await resp.text()).toContain("unstored_count: 2"); + + const panicResp = await mf.dispatchFetch(`${mfUrl}test-panic`); + expect(panicResp.status).toBe(500); + + const panicText = await panicResp.text(); + expect(panicText).toContain("Workers runtime canceled"); + + const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + expect(await normalResp.text()).toContain("unstored_count: 1"); + } + + // multiple requests after panic all succeed + { + const panicResp = await mf.dispatchFetch(`${mfUrl}test-panic`); + expect(panicResp.status).toBe(500); + + const requests = [ + mf.dispatchFetch(`${mfUrl}durable/hello`), + mf.dispatchFetch(`${mfUrl}durable/hello`), + mf.dispatchFetch(`${mfUrl}durable/hello`), + ]; + + const responses = await Promise.all(requests); + + for (let i = 0; i < responses.length; i++) { + const text = await responses[i].text(); + expect(responses[i].status).toBe(200); + expect(text).toContain("Hello from my-durable-object!"); + } + } + + // simultaneous requests during panic handling + { + const simultaneousRequests = [ + mf.dispatchFetch(`${mfUrl}test-panic`), // This will panic + mf.dispatchFetch(`${mfUrl}durable/hello`), // This should succeed after reinitialization + mf.dispatchFetch(`${mfUrl}durable/hello`), + ]; + + const responses = await Promise.all(simultaneousRequests); + + // should always have one error and one ok + let foundErrors = 0; + for (const response of responses) { + if (response.status === 500) { + expect(foundErrors).toBeLessThan(2); + foundErrors++; + } else { + expect(response.status).toBe(200); + } + } + expect(foundErrors).toBeGreaterThan(0); + } + + // worker continues to function normally after multiple panics + { + for (let cycle = 1; cycle <= 3; cycle++) { + const panicResp = await mf.dispatchFetch(`${mfUrl}test-panic`); + expect(panicResp.status).toBe(500); + + const recoveryResp = await mf.dispatchFetch(`${mfUrl}durable/hello`); + expect(recoveryResp.status).toBe(200); + } + } + + // explicit abort() recovery test + { + await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + expect(await resp.text()).toContain("unstored_count:"); + + const abortResp = await mf.dispatchFetch(`${mfUrl}test-abort`); + expect(abortResp.status).toBe(500); + + const abortText = await abortResp.text(); + expect(abortText).toContain("Workers runtime canceled"); + + const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + expect(await normalResp.text()).toContain("unstored_count: 1"); + } + + // out of memory recovery test + { + await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + expect(await resp.text()).toContain("unstored_count:"); + + const oomResp = await mf.dispatchFetch(`${mfUrl}test-oom`); + expect(oomResp.status).toBe(500); + + const oomText = await oomResp.text(); + expect(oomText).toContain("Workers runtime canceled"); + + const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + expect(await normalResp.text()).toContain("unstored_count: 1"); + } + + // JS error recovery test + // TODO: figure out how to achieve this one. Hard part is global error handler + // will need to detect JS errors, not just WebAssembly.RuntimeError, which + // may over-classify. + // { + // await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + // const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + // expect(await resp.text()).toContain("unstored_count:"); + + // const jsErrorResp = await mf.dispatchFetch(`${mfUrl}test-js-error`); + // expect(jsErrorResp.status).toBe(500); + + // const jsErrorText = await jsErrorResp.text(); + // expect(jsErrorText).toContain("Workers runtime canceled"); + + // const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`); + // expect(await normalResp.text()).toContain("unstored_count: 1"); + // } + }, 20_000); +}); diff --git a/worker-sandbox/tests/queue.spec.ts b/test/tests/queue.spec.ts similarity index 80% rename from worker-sandbox/tests/queue.spec.ts rename to test/tests/queue.spec.ts index ac364d34..ac3d4893 100644 --- a/worker-sandbox/tests/queue.spec.ts +++ b/test/tests/queue.spec.ts @@ -1,11 +1,11 @@ import { describe, test, expect } from "vitest"; import * as uuid from "uuid"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; describe("queue", () => { test("send message to queue", async () => { const resp = await mf.dispatchFetch( - `https://fake.host/queue/send/${uuid.v4()}`, + `${mfUrl}queue/send/${uuid.v4()}`, { method: "POST" } ); expect(resp.status).toBe(200); @@ -13,14 +13,14 @@ describe("queue", () => { test("receive message from queue", async () => { const id = uuid.v4(); - let resp = await mf.dispatchFetch(`https://fake.host/queue/send/${id}`, { + let resp = await mf.dispatchFetch(`${mfUrl}queue/send/${id}`, { method: "POST", }); expect(resp.status).toBe(200); await new Promise((resolve) => setTimeout(resolve, 1200)); - resp = await mf.dispatchFetch("https://fake.host/queue"); + resp = await mf.dispatchFetch(`${mfUrl}queue`); expect(resp.status).toBe(200); const messages = (await resp.json()) as { id: string }[]; @@ -32,7 +32,7 @@ describe("queue", () => { const id_1 = uuid.v4(); const id_2 = uuid.v4(); - let resp = await mf.dispatchFetch(`https://fake.host/queue/send_batch`, { + let resp = await mf.dispatchFetch(`${mfUrl}queue/send_batch`, { method: "POST", body: JSON.stringify([{ id: id_1, id_string: id_1 }, { id: id_2, id_string: id_2 }]) }); @@ -41,7 +41,7 @@ describe("queue", () => { await new Promise((resolve) => setTimeout(resolve, 1200)); - resp = await mf.dispatchFetch("https://fake.host/queue"); + resp = await mf.dispatchFetch(`${mfUrl}queue`); expect(resp.status).toBe(200); let body = await resp.json(); diff --git a/worker-sandbox/tests/r2.spec.ts b/test/tests/r2.spec.ts similarity index 57% rename from worker-sandbox/tests/r2.spec.ts rename to test/tests/r2.spec.ts index 7ba4d3f0..0b2a3a8b 100644 --- a/worker-sandbox/tests/r2.spec.ts +++ b/test/tests/r2.spec.ts @@ -1,43 +1,43 @@ import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; describe("r2", () => { test("list empty", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/list-empty"); + const resp = await mf.dispatchFetch(`${mfUrl}r2/list-empty`); expect(await resp.text()).toBe("ok"); }); test("list", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/list"); + const resp = await mf.dispatchFetch(`${mfUrl}r2/list`); expect(await resp.text()).toBe("ok"); }); test("get empty", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/get-empty"); + const resp = await mf.dispatchFetch(`${mfUrl}r2/get-empty`); expect(await resp.text()).toBe("ok"); }); test("get", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/get"); + const resp = await mf.dispatchFetch(`${mfUrl}r2/get`); expect(await resp.text()).toBe("ok"); }); test("put", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/put", { + const resp = await mf.dispatchFetch(`${mfUrl}r2/put`, { method: "put", }); expect(await resp.text()).toBe("ok"); }); test("put properties", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/put-properties", { + const resp = await mf.dispatchFetch(`${mfUrl}r2/put-properties`, { method: "put", }); expect(await resp.text()).toBe("ok"); }); test("delete", async () => { - const resp = await mf.dispatchFetch("https://fake.host/r2/delete", { + const resp = await mf.dispatchFetch(`${mfUrl}r2/delete`, { method: "delete", }); expect(await resp.text()).toBe("ok"); diff --git a/test/tests/rate_limit.spec.ts b/test/tests/rate_limit.spec.ts new file mode 100644 index 00000000..f86fa004 --- /dev/null +++ b/test/tests/rate_limit.spec.ts @@ -0,0 +1,169 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("rate limit", () => { + test("basic rate limit check", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}rate-limit/check`); + expect(resp.status).toBe(200); + const data = await resp.json() as { success: boolean }; + expect(data).toHaveProperty("success"); + expect(data.success).toBe(true); + }); + + test("rate limit with custom key", async () => { + const key = "test-key-123"; + const resp = await mf.dispatchFetch(`${mfUrl}rate-limit/key/${key}`); + expect(resp.status).toBe(200); + const data = await resp.json() as { success: boolean; key: string }; + expect(data).toHaveProperty("success"); + expect(data).toHaveProperty("key"); + expect(data.key).toBe(key); + expect(data.success).toBe(true); + }); + + test("different keys have independent limits", async () => { + // Test that different keys have separate rate limits + const key1 = "user-1"; + const key2 = "user-2"; + + const resp1 = await mf.dispatchFetch(`${mfUrl}rate-limit/key/${key1}`); + const resp2 = await mf.dispatchFetch(`${mfUrl}rate-limit/key/${key2}`); + + expect(resp1.status).toBe(200); + expect(resp2.status).toBe(200); + + const data1 = await resp1.json() as { success: boolean; key: string }; + const data2 = await resp2.json() as { success: boolean; key: string }; + + expect(data1.success).toBe(true); + expect(data2.success).toBe(true); + expect(data1.key).toBe(key1); + expect(data2.key).toBe(key2); + }); + + test("bulk rate limit test", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}rate-limit/bulk-test`); + expect(resp.status).toBe(200); + const data = await resp.json() as { results: Array<{ index: number; key: string; success: boolean }> }; + expect(data).toHaveProperty("results"); + expect(Array.isArray(data.results)).toBe(true); + expect(data.results.length).toBe(15); + + // Check that results have the expected structure + data.results.forEach((result, index: number) => { + expect(result).toHaveProperty("index"); + expect(result).toHaveProperty("key"); + expect(result).toHaveProperty("success"); + expect(result.index).toBe(index); + expect(typeof result.success).toBe("boolean"); + }); + + // We're using 3 different keys (bulk-test-0, bulk-test-1, bulk-test-2) + // with a limit of 10 per 60 seconds. Each key is used 5 times (15 requests / 3 keys). + // All requests should succeed since each key stays under the limit of 10. + + // Group results by key + const resultsByKey: Record> = {}; + data.results.forEach((result) => { + if (!resultsByKey[result.key]) { + resultsByKey[result.key] = []; + } + resultsByKey[result.key].push(result); + }); + + // Should have exactly 3 keys + expect(Object.keys(resultsByKey).length).toBe(3); + + // Each key should have 5 requests, all successful (under limit of 10) + Object.entries(resultsByKey).forEach(([key, results]) => { + expect(results.length).toBe(5); + results.forEach((result) => { + expect(result.success).toBe(true); + }); + }); + }); + + test("rate limit reset with unique keys", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}rate-limit/reset`); + expect(resp.status).toBe(200); + const data = await resp.json() as Record; + + // Should have 12 request results + expect(Object.keys(data).length).toBe(12); + + // Check that we have the expected keys + for (let i = 1; i <= 12; i++) { + expect(data).toHaveProperty(`request_${i}`); + expect(typeof data[`request_${i}`]).toBe("boolean"); + } + + // With a limit of 10 per 60 seconds, the first 10 requests MUST succeed + // and requests 11 and 12 MUST fail + for (let i = 1; i <= 10; i++) { + expect(data[`request_${i}`]).toBe(true); + } + + // Requests 11 and 12 must be rate limited + expect(data["request_11"]).toBe(false); + expect(data["request_12"]).toBe(false); + }); + + test("multiple rapid requests with same key", async () => { + // Generate a unique key for this test + const testKey = `rapid-test-${Date.now()}`; + + // Make multiple rapid requests with the same key + const promises = []; + for (let i = 0; i < 5; i++) { + promises.push(mf.dispatchFetch(`${mfUrl}rate-limit/key/${testKey}`)); + } + + const responses = await Promise.all(promises); + + // All responses should be successful (200 status) + responses.forEach(resp => { + expect(resp.status).toBe(200); + }); + + // Parse the responses + const results = await Promise.all(responses.map(r => r.json())) as Array<{ success: boolean; key: string }>; + + // All should have the same key + results.forEach(data => { + expect(data.key).toBe(testKey); + expect(data).toHaveProperty("success"); + }); + + // With limit of 10, all 5 requests should succeed + results.forEach((data) => { + expect(data.success).toBe(true); + }); + }); + + test("sequential requests enforce rate limit", async () => { + // Generate a unique key for this test to avoid interference + const testKey = `sequential-test-${Date.now()}`; + + // Make 15 sequential requests with the same key + // With a limit of 10 per 60 seconds, first 10 should succeed, rest should fail + const results: Array<{ success: boolean; key: string }> = []; + for (let i = 0; i < 15; i++) { + const resp = await mf.dispatchFetch(`${mfUrl}rate-limit/key/${testKey}`); + expect(resp.status).toBe(200); + const data = await resp.json() as { success: boolean; key: string }; + results.push(data); + } + + // Verify first 10 requests succeed + for (let i = 0; i < 10; i++) { + expect(results[i].success).toBe(true); + expect(results[i].key).toBe(testKey); + } + + // Verify requests 11-15 are rate limited + for (let i = 10; i < 15; i++) { + expect(results[i].success).toBe(false); + expect(results[i].key).toBe(testKey); + } + }); +}); diff --git a/worker-sandbox/tests/request.spec.ts b/test/tests/request.spec.ts similarity index 66% rename from worker-sandbox/tests/request.spec.ts rename to test/tests/request.spec.ts index 6f648887..4745d9ea 100644 --- a/worker-sandbox/tests/request.spec.ts +++ b/test/tests/request.spec.ts @@ -1,25 +1,25 @@ import { expect, test } from "vitest"; import { FormData } from "miniflare"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; test("basic sync request", async () => { - const resp = await mf.dispatchFetch("https://fake.host/request"); + const resp = await mf.dispatchFetch(`${mfUrl}request`); expect(resp.status).toBe(200); }); test("basic async request", async () => { - const resp = await mf.dispatchFetch("https://fake.host/async-request"); + const resp = await mf.dispatchFetch(`${mfUrl}async-request`); expect(resp.status).toBe(200); }); test("test data", async () => { - const resp = await mf.dispatchFetch("https://fake.host/test-data"); + const resp = await mf.dispatchFetch(`${mfUrl}test-data`); expect(await resp.text()).toBe("data ok"); }); test("headers", async () => { - const resp = await mf.dispatchFetch("https://fake.host/headers", { + const resp = await mf.dispatchFetch(`${mfUrl}headers`, { method: "POST", headers: { A: "B", @@ -33,7 +33,7 @@ test("secret", async () => { const formData = new FormData(); formData.append("secret", "EXAMPLE_SECRET"); - const resp = await mf.dispatchFetch("https://fake.host/is-secret", { + const resp = await mf.dispatchFetch(`${mfUrl}is-secret`, { method: "POST", body: formData, }); @@ -45,7 +45,7 @@ test("form data", async () => { const formData = new FormData(); formData.append("file", new Blob(["workers-rs is cool"]), "file"); - let resp = await mf.dispatchFetch("https://fake.host/formdata-file-size", { + let resp = await mf.dispatchFetch(`${mfUrl}formdata-file-size`, { method: "POST", body: formData, }); @@ -54,24 +54,24 @@ test("form data", async () => { const hashes = (await resp.json()) as { name: string }[]; resp = await mf.dispatchFetch( - `https://fake.host/formdata-file-size/${hashes[0].name}` + `${mfUrl}formdata-file-size/${hashes[0].name}` ); expect(resp.status).toBe(200); }); test("user id", async () => { - const resp = await mf.dispatchFetch("https://fake.host/user/example/test"); + const resp = await mf.dispatchFetch(`${mfUrl}user/example/test`); expect(await resp.text()).toBe("TEST user id: example"); }); test("user", async () => { - const resp = await mf.dispatchFetch("https://fake.host/user/example"); + const resp = await mf.dispatchFetch(`${mfUrl}user/example`); expect(await resp.json()).toMatchObject({ id: "example" }); }); test("post account id zones", async () => { const resp = await mf.dispatchFetch( - "https://fake.host/account/example/zones", + `${mfUrl}account/example/zones`, { method: "POST", } @@ -81,7 +81,7 @@ test("post account id zones", async () => { test("get account id zones", async () => { const resp = await mf.dispatchFetch( - "https://fake.host/account/example/zones" + `${mfUrl}account/example/zones` ); expect(await resp.text()).toBe( "Account id: example..... You get a zone, you get a zone!" @@ -89,7 +89,7 @@ test("get account id zones", async () => { }); test("async text echo", async () => { - const resp = await mf.dispatchFetch("https://fake.host/async-text-echo", { + const resp = await mf.dispatchFetch(`${mfUrl}async-text-echo`, { method: "POST", body: "Example text!", }); @@ -97,34 +97,44 @@ test("async text echo", async () => { }); test("fetch", async () => { - const resp = await mf.dispatchFetch("https://fake.host/fetch"); + const resp = await mf.dispatchFetch(`${mfUrl}fetch`); expect(resp.status).toBe(200); }); test("fetch json", async () => { - const resp = await mf.dispatchFetch("https://fake.host/fetch_json"); + const resp = await mf.dispatchFetch(`${mfUrl}fetch_json`); expect(resp.status).toBe(200); }); test("proxy request", async () => { const resp = await mf.dispatchFetch( - "https://fake.host/proxy_request/https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding/contributors.txt" + `${mfUrl}proxy_request/https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Encoding/contributors.txt` ); expect(resp.status).toBe(200); }); test("durable id", async () => { - const resp = await mf.dispatchFetch("https://fake.host/durable/example"); + const resp = await mf.dispatchFetch(`${mfUrl}durable/example`); expect(await resp.text()).toContain("[durable_object]"); }); test("some secret", async () => { - const resp = await mf.dispatchFetch("https://fake.host/secret"); + const resp = await mf.dispatchFetch(`${mfUrl}secret`); expect(await resp.text()).toBe("secret!"); }); +test("some var", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}var`); + expect(await resp.text()).toBe("some value"); +}); + +test("some object var", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}object-var`); + expect(await resp.json()).toStrictEqual({ foo: 42, bar: "string" }); +}); + test("kv key value", async () => { - const resp = await mf.dispatchFetch("https://fake.host/kv/a/b", { + const resp = await mf.dispatchFetch(`${mfUrl}kv/a/b`, { method: "POST", }); @@ -133,7 +143,7 @@ test("kv key value", async () => { }); test("bytes", async () => { - const resp = await mf.dispatchFetch("https://fake.host/bytes"); + const resp = await mf.dispatchFetch(`${mfUrl}bytes`); expect(await resp.arrayBuffer()).toStrictEqual( new Uint8Array([1, 2, 3, 4, 5, 6, 7]).buffer ); @@ -141,7 +151,7 @@ test("bytes", async () => { test("api data", async () => { const data = { userId: 0, title: "Hi!", completed: true }; - const resp = await mf.dispatchFetch("https://fake.host/api-data", { + const resp = await mf.dispatchFetch(`${mfUrl}api-data`, { method: "POST", headers: { "content-type": "application/json", @@ -156,7 +166,7 @@ test("api data", async () => { }); test("nonsense repeat", async () => { - const resp = await mf.dispatchFetch("https://fake.host/nonsense-repeat", { + const resp = await mf.dispatchFetch(`${mfUrl}nonsense-repeat`, { method: "POST", }); @@ -164,12 +174,12 @@ test("nonsense repeat", async () => { }); test("status code", async () => { - const resp = await mf.dispatchFetch("https://fake.host/status/418"); + const resp = await mf.dispatchFetch(`${mfUrl}status/418`); expect(resp.status).toBe(418); }); test("root", async () => { - const resp = await mf.dispatchFetch("https://fake.host/", { + const resp = await mf.dispatchFetch(mfUrl, { method: "PUT", }); @@ -177,7 +187,7 @@ test("root", async () => { }); test("async", async () => { - const resp = await mf.dispatchFetch("https://fake.host/async", { + const resp = await mf.dispatchFetch(`${mfUrl}async`, { method: "PUT", }); @@ -185,7 +195,7 @@ test("async", async () => { }); test("catchall", async () => { - const resp = await mf.dispatchFetch("https://fake.host/hello-world", { + const resp = await mf.dispatchFetch(`${mfUrl}hello-world`, { method: "OPTIONS", }); @@ -193,14 +203,14 @@ test("catchall", async () => { }); test("redirect default", async () => { - const resp = await mf.dispatchFetch("https://fake.host/redirect-default", { + const resp = await mf.dispatchFetch(`${mfUrl}redirect-default`, { redirect: "manual", }); expect(resp.headers.get("location")).toBe("https://example.com/"); }); test("redirect 307", async () => { - const resp = await mf.dispatchFetch("https://fake.host/redirect-307", { + const resp = await mf.dispatchFetch(`${mfUrl}redirect-307`, { redirect: "manual", }); expect(resp.headers.get("location")).toBe("https://example.com/"); @@ -208,26 +218,26 @@ test("redirect 307", async () => { }); test("now", async () => { - const resp = await mf.dispatchFetch("https://fake.host/now"); + const resp = await mf.dispatchFetch(`${mfUrl}now`); expect(resp.status).toBe(200); }); test("wait", async () => { const then = Date.now(); - const resp = await mf.dispatchFetch("https://fake.host/wait/100"); + const resp = await mf.dispatchFetch(`${mfUrl}wait/100`); expect(resp.status).toBe(200); expect(Date.now() - then).toBeGreaterThan(100); }); test("custom response body", async () => { const then = Date.now(); - const resp = await mf.dispatchFetch("https://fake.host/wait/100"); + const resp = await mf.dispatchFetch(`${mfUrl}wait/100`); expect(resp.status).toBe(200); expect(Date.now() - then).toBeGreaterThan(100); }); test("init called", async () => { - const resp = await mf.dispatchFetch("https://fake.host/init-called"); + const resp = await mf.dispatchFetch(`${mfUrl}init-called`); expect(await resp.text()).toBe("true"); }); @@ -238,7 +248,7 @@ test("xor", async () => { } } - const resp = await mf.dispatchFetch("https://fake.host/xor/10", { + const resp = await mf.dispatchFetch(`${mfUrl}xor/10`, { method: "POST", body: generator(), duplex: "half", diff --git a/test/tests/secret_store.spec.ts b/test/tests/secret_store.spec.ts new file mode 100644 index 00000000..578858cc --- /dev/null +++ b/test/tests/secret_store.spec.ts @@ -0,0 +1,14 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("secret store", () => { + test("secret store", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}get-from-secret-store`); + expect(await resp.text()).toBe("secret value"); + }); + + test("get_from_secret_store_missing", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}get-from-secret-store-missing`); + expect(resp.status).toBe(500); + }); +}); diff --git a/worker-sandbox/tests/service-bindings.spec.ts b/test/tests/service-bindings.spec.ts similarity index 59% rename from worker-sandbox/tests/service-bindings.spec.ts rename to test/tests/service-bindings.spec.ts index 7c6af461..44875373 100644 --- a/worker-sandbox/tests/service-bindings.spec.ts +++ b/test/tests/service-bindings.spec.ts @@ -1,14 +1,14 @@ import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; describe("service bindings", () => { test("by path", async () => { - const resp = await mf.dispatchFetch("https://fake.host/remote-by-path"); + const resp = await mf.dispatchFetch(`${mfUrl}remote-by-path`); expect(await resp.text()).toBe("hello world"); }); test("by request", async () => { - const resp = await mf.dispatchFetch("https://fake.host/remote-by-request"); + const resp = await mf.dispatchFetch(`${mfUrl}remote-by-request`); expect(await resp.text()).toBe("hello world"); }); }); diff --git a/test/tests/snippets.spec.ts b/test/tests/snippets.spec.ts new file mode 100644 index 00000000..74887f6c --- /dev/null +++ b/test/tests/snippets.spec.ts @@ -0,0 +1,16 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("JS Snippets", () => { + test("performance.now", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}js_snippets/now`); + const text = await resp.text(); + expect(text).toMatch(/^now: \d+$$/); + }); + + test("console.log", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}js_snippets/log`); + const text = await resp.text(); + expect(text).toBe("OK"); + }); +}); diff --git a/worker-sandbox/tests/socket.spec.ts b/test/tests/socket.spec.ts similarity index 54% rename from worker-sandbox/tests/socket.spec.ts rename to test/tests/socket.spec.ts index 62fa4117..89a6fb6e 100644 --- a/worker-sandbox/tests/socket.spec.ts +++ b/test/tests/socket.spec.ts @@ -1,13 +1,13 @@ import {describe, test, expect} from "vitest"; -import { mf } from "./mf-socket"; +import { mf, mfUrl } from "./mf-socket"; describe("socket", () => { test("failed", async () => { - const resp = await mf.dispatchFetch("https://fake.host/socket/failed"); + const resp = await mf.dispatchFetch(`${mfUrl}socket/failed`); expect(resp.status).toBe(200); }); test("read", async () => { - const resp = await mf.dispatchFetch("https://fake.host/socket/read"); + const resp = await mf.dispatchFetch(`${mfUrl}socket/read`); expect(resp.status).toBe(200); }); }); \ No newline at end of file diff --git a/test/tests/sql_iterator.spec.ts b/test/tests/sql_iterator.spec.ts new file mode 100644 index 00000000..63c3d818 --- /dev/null +++ b/test/tests/sql_iterator.spec.ts @@ -0,0 +1,274 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +const productNames = ["Laptop", "Mouse", "Keyboard", "Monitor", "Headphones"]; +const expectedTypedStrings = [ + "Product 1: Laptop - $999.99 (in stock: true)", + "Product 2: Mouse - $29.99 (in stock: true)", + "Product 3: Keyboard - $79.99 (in stock: false)", + "Product 4: Monitor - $299.99 (in stock: true)", + "Product 5: Headphones - $149.99 (in stock: false)", +]; + +describe("sql iterator durable object", () => { + test("next() iterator returns typed results", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/test/next`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("next() iterator results:"); + + // Check that we get all 5 products + for (const s of expectedTypedStrings) { + expect(text).toContain(s); + } + }); + + test("raw() iterator returns column names and raw values", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/test/raw`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("raw() iterator results:"); + + // Check column names are included + expect(text).toContain("Columns: id, name, price, in_stock"); + + // Check that we get raw data rows - should contain all products + for (const name of productNames) { + expect(text).toContain(`String("${name}")`); + } + + // Check for different data types in raw format + expect(text).toContain("Integer("); // For IDs + expect(text).toContain("Float(999.99)"); + expect(text).toContain("Integer(1)"); // in_stock: true + expect(text).toContain("Integer(0)"); // in_stock: false + }); + + test("different object instances have independent data", async () => { + // Test first instance + const resp1 = await mf.dispatchFetch( + `${mfUrl}sql-iterator/instance1/next`, + ); + expect(resp1.status).toBe(200); + const text1 = await resp1.text(); + expect(text1).toContain("Product 1: Laptop"); + + // Test second instance - should have same seeded data + const resp2 = await mf.dispatchFetch( + `${mfUrl}sql-iterator/instance2/next`, + ); + expect(resp2.status).toBe(200); + const text2 = await resp2.text(); + expect(text2).toContain("Product 1: Laptop"); + + // Both should be identical since they use the same seed data + expect(text1).toBe(text2); + }); + + test("iterator handles empty results gracefully", async () => { + // Create a new instance to test empty query behavior + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/empty-test/raw`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + // Should still show column names even with data + expect(text).toContain("Columns: id, name, price, in_stock"); + }); + + test("next() iterator handles deserialization errors", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/test/next-invalid`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("next-invalid() iterator results:"); + + // Should have 5 error messages (one for each row that failed deserialization) + const deserializationErrors = text.match(/Error deserializing row:/g); + expect(deserializationErrors).toBeTruthy(); + expect(deserializationErrors!.length).toBe(5); + + // Check that the error messages contain information about the type mismatch + expect(text).toContain("invalid type"); + }); + + test.each([ + ["root", ""], + ["invalid", "/invalid"], + ])("%s endpoint returns help message", async (_, path) => { + const resp = await mf.dispatchFetch( + `http://fake.host/sql-iterator/test${path}`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toBe("SQL Iterator Test - try /next, /raw, /next-invalid, /blob-next, /blob-raw, or /blob-roundtrip endpoints"); + }); + + test("data consistency between next() and raw() methods", async () => { + // Get data from next() method + const nextResp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/consistency-test/next`, + ); + expect(nextResp.status).toBe(200); + const nextText = await nextResp.text(); + + // Get data from raw() method + const rawResp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/consistency-test/raw`, + ); + expect(rawResp.status).toBe(200); + const rawText = await rawResp.text(); + + // Both should contain the same 5 products + for (const name of productNames) { + expect(nextText).toContain(name); + expect(rawText).toContain(name); + } + + // Raw should show 5 data rows (plus column header) + const rowMatches = rawText.match(/Row: \[/g); + expect(rowMatches).toBeTruthy(); + expect(rowMatches!.length).toBe(5); + }); + + describe("BLOB handling", () => { + test("blob-next() iterator returns BLOB data correctly", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/blob-test/blob-next`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("blob-next() iterator results:"); + + // Check for various BLOB test cases + expect(text).toContain("binary_data"); // Binary data test case + expect(text).toContain("empty_blob"); // Empty blob test case + expect(text).toContain("text_as_blob"); // Text converted to blob + expect(text).toContain("large_blob"); // Large blob test case + + // Check that binary data is displayed correctly + expect(text).toContain("[0, 1, 2, 3, 255, 254]"); + + // Check empty blob handling + expect(text).toContain("data: []"); + + // Check large blob truncation + expect(text).toContain("[1000 bytes total]"); + }); + + test("blob-raw() iterator returns raw BLOB values", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/blob-test/blob-raw`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("blob-raw() iterator results:"); + + // Check column names are included + expect(text).toContain("Columns: id, name, data"); + + // Check BLOB data in raw format + expect(text).toContain("Blob([0, 1, 2, 3, 255, 254])"); + expect(text).toContain("Blob([])"); // Empty blob + expect(text).toContain("Blob([72, 101, 108, 108, 111"); // "Hello" bytes + + // Check large blob truncation in raw format + expect(text).toMatch(/Blob\(\[0, 1, 2, 3, 4, 5, 6, 7, 8, 9\]\.\.\..*1000 bytes/); + }); + + test("blob roundtrip test verifies data integrity", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/roundtrip-test/blob-roundtrip`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("blob-roundtrip test results:"); + + // Check that original data is shown + expect(text).toContain("Original data: [222, 173, 190, 239, 0, 255]"); + + // Check that both next() and raw() methods return matching data + expect(text).toContain("next() result: [222, 173, 190, 239, 0, 255], matches_original: true"); + expect(text).toContain("raw() result: [222, 173, 190, 239, 0, 255], matches_original: true"); + }); + + test("BLOB data consistency between next() and raw() methods", async () => { + // Get data from blob-next() method + const nextResp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/consistency-blob-test/blob-next`, + ); + expect(nextResp.status).toBe(200); + const nextText = await nextResp.text(); + + // Get data from blob-raw() method + const rawResp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/consistency-blob-test/blob-raw`, + ); + expect(rawResp.status).toBe(200); + const rawText = await rawResp.text(); + + // Both should contain the same BLOB test data + const blobNames = ["binary_data", "empty_blob", "text_as_blob", "large_blob"]; + for (const name of blobNames) { + expect(nextText).toContain(name); + expect(rawText).toContain(name); + } + + // Both should show 4 BLOB data rows + const nextRows = nextText.match(/BlobData \d+:/g); + const rawRows = rawText.match(/Row: \[/g); + expect(nextRows).toBeTruthy(); + expect(rawRows).toBeTruthy(); + expect(nextRows!.length).toBe(4); + expect(rawRows!.length).toBe(4); + }); + + test("empty BLOB handling", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/empty-blob-test/blob-next`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("empty_blob"); + expect(text).toContain("data: []"); + }); + + test("large BLOB handling", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/large-blob-test/blob-raw`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("large_blob"); + // Large blob should be truncated for display + expect(text).toMatch(/Blob\(\[.*\]\.\.\..*1000 bytes/); + }); + + test("binary data with null bytes handling", async () => { + const resp = await mf.dispatchFetch( + `${mfUrl}sql-iterator/binary-test/blob-next`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("binary_data"); + // Should handle null bytes (0x00) and high bytes (0xFF) correctly + expect(text).toContain("[0, 1, 2, 3, 255, 254]"); + }); + }); +}); diff --git a/test/tests/sqlite.spec.ts b/test/tests/sqlite.spec.ts new file mode 100644 index 00000000..66bfbe3e --- /dev/null +++ b/test/tests/sqlite.spec.ts @@ -0,0 +1,69 @@ +import { describe, test, expect } from "vitest"; +import { mf, mfUrl } from "./mf"; + +describe("sqlite durable object", () => { + test("counter increments per object", async () => { + // First access for object "alice" + let resp = await mf.dispatchFetch(`${mfUrl}sql-counter/alice`); + expect(resp.status).toBe(200); + expect(await resp.text()).toBe("SQL counter is now 1"); + + // Second access for same object should increment + resp = await mf.dispatchFetch(`${mfUrl}sql-counter/alice`); + expect(resp.status).toBe(200); + expect(await resp.text()).toBe("SQL counter is now 2"); + + // Different object name should have its own counter starting at 1 + resp = await mf.dispatchFetch(`${mfUrl}sql-counter/bob`); + expect(resp.status).toBe(200); + expect(await resp.text()).toBe("SQL counter is now 1"); + }); + + test("try_from_i64 handles safe values", async () => { + // Test with a safe large value (within JavaScript safe integer range) + const safeValue = "9007199254740991"; // 2^53 - 1 (MAX_SAFE_INTEGER) + const resp = await mf.dispatchFetch( + `http://fake.host/sql-counter/safe-test/set-large/${safeValue}`, + ); + expect(resp.status).toBe(200); + expect(await resp.text()).toBe(`Successfully stored large value: ${safeValue}`); + }); + + test("try_from_i64 rejects unsafe values", async () => { + // Test with value exceeding JavaScript safe integer range + const unsafeValue = "9007199254740992"; // 2^53 (exceeds MAX_SAFE_INTEGER) + const resp = await mf.dispatchFetch( + `http://fake.host/sql-counter/unsafe-test/set-large/${unsafeValue}`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("Error: Cannot store value"); + expect(text).toContain(unsafeValue); + expect(text).toContain("JavaScript safe range is ±9007199254740991"); + }); + + test("try_from_i64 handles negative safe values", async () => { + // Test with safe negative value + const safeNegativeValue = "-9007199254740991"; // -(2^53 - 1) (MIN_SAFE_INTEGER) + const resp = await mf.dispatchFetch( + `http://fake.host/sql-counter/safe-negative-test/set-large/${safeNegativeValue}`, + ); + expect(resp.status).toBe(200); + expect(await resp.text()).toBe(`Successfully stored large value: ${safeNegativeValue}`); + }); + + test("try_from_i64 rejects unsafe negative values", async () => { + // Test with negative value exceeding JavaScript safe integer range + const unsafeNegativeValue = "-9007199254740992"; // -(2^53) (exceeds MIN_SAFE_INTEGER) + const resp = await mf.dispatchFetch( + `http://fake.host/sql-counter/unsafe-negative-test/set-large/${unsafeNegativeValue}`, + ); + expect(resp.status).toBe(200); + + const text = await resp.text(); + expect(text).toContain("Error: Cannot store value"); + expect(text).toContain(unsafeNegativeValue); + expect(text).toContain("JavaScript safe range is ±9007199254740991"); + }); +}); diff --git a/worker-sandbox/tests/subrequest.spec.ts b/test/tests/subrequest.spec.ts similarity index 64% rename from worker-sandbox/tests/subrequest.spec.ts rename to test/tests/subrequest.spec.ts index 83e1ec2c..5d62a390 100644 --- a/worker-sandbox/tests/subrequest.spec.ts +++ b/test/tests/subrequest.spec.ts @@ -1,25 +1,25 @@ import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; +import { mf, mfUrl } from "./mf"; describe("subrequest", () => { test("request init fetch", async () => { - const resp = await mf.dispatchFetch("https://fake.host/request-init-fetch"); + const resp = await mf.dispatchFetch(`${mfUrl}request-init-fetch`); expect(resp.status).toBe(200); }); test("cancelled fetch", async () => { - const resp = await mf.dispatchFetch("https://fake.host/cancelled-fetch"); + const resp = await mf.dispatchFetch(`${mfUrl}cancelled-fetch`); expect(await resp.text()).toContain("AbortError"); }); test("fetch timeout", async () => { - const resp = await mf.dispatchFetch("https://fake.host/fetch-timeout"); + const resp = await mf.dispatchFetch(`${mfUrl}fetch-timeout`); expect(await resp.text()).toBe("Cancelled"); }); test.skip("request init fetch post", async () => { const resp = await mf.dispatchFetch( - "https://fake.host/request-init-fetch-post" + `${mfUrl}request-init-fetch-post` ); expect(await resp.json()).toMatchObject({ url: "https://httpbin.org/post", diff --git a/test/tests/websocket.spec.ts b/test/tests/websocket.spec.ts new file mode 100644 index 00000000..04f64c6b --- /dev/null +++ b/test/tests/websocket.spec.ts @@ -0,0 +1,36 @@ +import { describe, expect, test } from "vitest"; +import { MessageEvent } from "miniflare"; +import { mf, mfUrl } from "./mf"; + +describe("websocket", () => { + test("to echo", async () => { + const resp = await mf.dispatchFetch(`${mfUrl}websocket`, { + headers: { + upgrade: "websocket", + }, + }); + expect(resp.webSocket).not.toBeNull(); + + const socket = resp.webSocket!; + socket.accept(); + + let cnt = 0; + socket.addEventListener("message", function (_event: MessageEvent) { + cnt++; + }); + let calledClose = false; + socket.addEventListener("close", function (_event: CloseEvent) { + calledClose = true; + }); + + socket.send("Hello, world!"); + + await new Promise((resolve) => setTimeout(resolve, 500)); + + expect(cnt).toBe(1); + socket.close(); + + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(calledClose).toBe(true); + }); +}); diff --git a/test/wrangler.toml b/test/wrangler.toml new file mode 100644 index 00000000..cf9d3527 --- /dev/null +++ b/test/wrangler.toml @@ -0,0 +1,91 @@ +name = "testing-rust-worker" +workers_dev = true +compatibility_date = "2025-09-23" # required +main = "build/worker/shim.mjs" + +kv_namespaces = [ + { binding = "SOME_NAMESPACE", id = "SOME_NAMESPACE", preview_id = "SOME_NAMESPACE" }, + { binding = "FILE_SIZES", id = "FILE_SIZES", preview_id = "FILE_SIZES" }, +] + +[vars] +SOME_VARIABLE = "some value" +SOME_OBJECT_VARIABLE = { foo = 42, bar = "string" } + +[assets] +binding = "ASSETS" +directory = "./public/" + +[[services]] +binding = "remote" +service = "remote-service" + +[durable_objects] +bindings = [ + { name = "COUNTER", class_name = "Counter" }, + { name = "ALARM", class_name = "AlarmObject" }, + { name = "PUT_RAW_TEST_OBJECT", class_name = "PutRawTestObject" }, + { name = "AUTO", class_name = "AutoResponseObject" }, + { name = "SQL_COUNTER", class_name = "SqlCounter" }, + { name = "SQL_ITERATOR", class_name = "SqlIterator" }, + { name = "MY_CLASS", class_name = "MyClass" }, + { name = "ECHO_CONTAINER", class_name = "EchoContainer" }, +] + +[[analytics_engine_datasets]] +dataset = "http" +binding = "HTTP_ANALYTICS" + +[[d1_databases]] +binding = 'DB' +database_name = 'my_db' +database_id = 'test' +preview_database_id = 'preview-test' + +[[queues.consumers]] +queue = "my_queue" + +[[queues.producers]] +queue = "my_queue" +binding = "my_queue" +[[r2_buckets]] +binding = 'EMPTY_BUCKET' +bucket_name = 'empty-bucket' +preview_bucket_name = 'empty-bucket' + +[[r2_buckets]] +binding = 'PUT_BUCKET' +bucket_name = 'put-bucket' +preview_bucket_name = 'put-bucket' + +[[r2_buckets]] +binding = 'SEEDED_BUCKET' +bucket_name = 'seeded-bucket' +preview_bucket_name = 'seeded-bucket' + +[[r2_buckets]] +binding = 'DELETE_BUCKET' +bucket_name = 'delete-bucket' +preview_bucket_name = 'delete-bucket' + +[build] +command = "WASM_BINDGEN_PATH=../wasm-bindgen/target/debug/wasm-bindgen ../target/debug/worker-build --release" + +[[migrations]] +tag = "v1" +new_sqlite_classes = ["SqlCounter", "SqlIterator", "EchoContainer"] + +[[secrets_store_secrets]] +binding = "SECRETS" +store_id = "SECRET_STORE" +secret_name = "secret-name" + +[[env.test.containers]] +class_name = "EchoContainer" +image = "./container-echo/Dockerfile" +max_instances = 1 + +[[ratelimits]] +name = "TEST_RATE_LIMITER" +namespace_id = "1" +simple = { limit = 10, period = 60 } diff --git a/tsconfig.json b/tsconfig.json index bed35c1d..12cb4840 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ /* Modules */ "module": "ES2022", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ diff --git a/wasm-bindgen b/wasm-bindgen new file mode 160000 index 00000000..f72a9d68 --- /dev/null +++ b/wasm-bindgen @@ -0,0 +1 @@ +Subproject commit f72a9d68d90cd75513e18859403f0139cc6913a1 diff --git a/worker-build/Cargo.toml b/worker-build/Cargo.toml index e96dec88..b15a95dc 100644 --- a/worker-build/Cargo.toml +++ b/worker-build/Cargo.toml @@ -2,28 +2,37 @@ authors = ["Cloudflare Workers Team "] edition = "2018" name = "worker-build" -version = "0.1.0" +version = "0.1.13" license = "Apache-2.0" repository = "https://github.com/cloudflare/workers-rs/tree/main/worker-build" readme = "README.md" description = "This is a tool to be used as a custom build command for a Cloudflare Workers `workers-rs` project." -[package.metadata.release] -release = false - [dependencies] -anyhow = "1.0.71" -dirs-next = "2.0.0" -flate2 = "1.0.26" -tar = "0.4.38" -ureq = { version = "2.6.2", features = ["tls", "gzip"] } -clap= { version="4.5", features=['derive'] } -worker-codegen = { path="../worker-codegen", version="0.1.0" } -wasm-pack = "0.13" +anyhow = "1.0.98" +binary-install = "0.4.1" +cargo_metadata = "0.20.0" +clap = { version = "4.5", features = ['derive'] } +console = "0.16.0" +dirs-next = "2.0" +flate2 = "1.1" +glob = "0.3.1" +log = "0.4.17" +parking_lot = "0.12.1" +path-clean = "1.0.1" +semver = "1.0.26" +serde = "1.0.219" +serde_ignored = "0.1.12" +serde_json = "1.0.143" +strsim = "0.11.1" +tar = "0.4" +toml = "0.9.5" +ureq = { version = "3.1", features = ["gzip", "json"] } +which = "8.0.0" +worker-codegen.workspace = true [dev-dependencies] wasm-bindgen-cli-support.workspace = true - [[bin]] -name="worker-codegen" +name = "worker-codegen" diff --git a/worker-build/src/install.rs b/worker-build/src/install.rs index 59a12958..0297c164 100644 --- a/worker-build/src/install.rs +++ b/worker-build/src/install.rs @@ -1,61 +1,68 @@ -use std::{fs::OpenOptions, io::Write, path::PathBuf}; +use std::{ + fs::{create_dir_all, read_dir, remove_file, OpenOptions}, + io::Write, + path::{Path, PathBuf}, +}; -use anyhow::Result; +use anyhow::{Context, Result}; use flate2::read::GzDecoder; -/// Checks if a binary with the specified name is on the user's path. -pub fn is_installed(name: &str) -> Result> { - let path = std::env::var_os("PATH").expect("could not read PATH environment variable"); - let path_directories = std::env::split_paths(&path).filter_map(|path| { - std::fs::metadata(&path) - .ok() - .map(|meta| meta.is_dir()) - .unwrap_or(false) - .then_some(path) - }); - - for dir in path_directories { - for entry in dir.read_dir()? { - let entry = entry?; - let file_type = entry.file_type()?; - let is_file_or_symlink = file_type.is_symlink() || file_type.is_file(); - - if is_file_or_symlink - && entry.file_name() == format!("{name}{BINARY_EXTENSION}").as_str() - { - return Ok(Some(entry.path())); +fn cache_path(name: &str) -> Result { + let path = dirs_next::cache_dir() + .unwrap_or_else(std::env::temp_dir) + .join("worker-build") + .join(name); + let parent = path.parent().unwrap(); + if !parent.exists() { + create_dir_all(parent)?; + } + Ok(path) +} + +fn remove_files_with_prefix(path: &Path) -> Result { + let dir = path.parent().unwrap_or(Path::new(".")); + let prefix = path.file_name().unwrap().to_str().unwrap(); + + let mut deleted_count = 0; + + for entry in read_dir(dir)? { + let entry = entry?; + let file_name = entry.file_name(); + + if let Some(name_str) = file_name.to_str() { + if name_str.starts_with(prefix) { + remove_file(entry.path())?; + deleted_count += 1; } } } - Ok(None) + Ok(deleted_count) } -const ESBUILD_VERSION: &str = "0.14.47"; +const ESBUILD_VERSION: &str = "0.25.10"; const BINARY_EXTENSION: &str = if cfg!(windows) { ".exe" } else { "" }; pub fn ensure_esbuild() -> Result { - // If we already have it we can skip the download. - if let Some(path) = is_installed("esbuild")? { - return Ok(path); - }; + let esbuild_prefix = format!("esbuild-{}{BINARY_EXTENSION}", esbuild_platform_pkg()); - let esbuild_binary = format!("esbuild-{}{BINARY_EXTENSION}", platform()); - let esbuild_bin_path = dirs_next::cache_dir() - .unwrap_or_else(std::env::temp_dir) - .join(esbuild_binary); + let esbuild_binary = format!("{esbuild_prefix}-{ESBUILD_VERSION}"); + + let esbuild_bin_path = cache_path(&esbuild_binary)?; if esbuild_bin_path.exists() { return Ok(esbuild_bin_path); } - let mut options = &mut std::fs::OpenOptions::new(); + // Clear old versions cache + remove_files_with_prefix(&cache_path(&esbuild_prefix)?)?; + let mut options = &mut std::fs::OpenOptions::new(); options = fix_permissions(options); let mut file = options.create(true).write(true).open(&esbuild_bin_path)?; - println!("Installing esbuild..."); + println!("Installing esbuild {ESBUILD_VERSION}..."); if let Err(e) = download_esbuild(&mut file) { // Make sure we close the file before we remove it. @@ -70,11 +77,14 @@ pub fn ensure_esbuild() -> Result { fn download_esbuild(writer: &mut impl Write) -> Result<()> { let esbuild_url = format!( - "https://registry.npmjs.org/esbuild-%7B0%7D/-/esbuild-%7B0%7D-%7BESBUILD_VERSION%7D.tgz", - platform() + "https://registry.npmjs.org/@esbuild/%7B0%7D/-/%7B0%7D-%7BESBUILD_VERSION%7D.tgz", + esbuild_platform_pkg() ); - let body = ureq::get(&esbuild_url).call()?.into_reader(); + let mut res = ureq::get(&esbuild_url) + .call() + .with_context(|| format!("Failed to fetch URL {esbuild_url}"))?; + let body = res.body_mut().as_reader(); let deflater = GzDecoder::new(body); let mut archive = tar::Archive::new(deflater); @@ -108,17 +118,29 @@ fn fix_permissions(options: &mut OpenOptions) -> &mut OpenOptions { /// Converts the user's platform from their Rust representation to their esbuild representation. /// https://esbuild.github.io/getting-started/#download-a-build -pub fn platform() -> &'static str { +pub fn esbuild_platform_pkg() -> &'static str { match (std::env::consts::OS, std::env::consts::ARCH) { - ("macos", "x86_64") => "darwin-64", + ("android", "arm") => "android-arm", + ("android", "aarch64") => "android-arm64", + ("android", "x86_64") => "android-x64", ("macos", "aarch64") => "darwin-arm64", - ("linux", "x86") => "linux-32", - ("linux", "x86_64") => "linux-64", + ("macos", "x86_64") => "darwin-x64", + ("freebsd", "aarch64") => "freebsd-arm64", + ("freebsd", "x86_64") => "freebsd-x64", ("linux", "arm") => "linux-arm", ("linux", "aarch64") => "linux-arm64", - ("windows", "x86") => "windows-32", - ("windows", "x86_64") => "windows-64", - ("windows", "aarch64") => "windows-arm64", + ("linux", "x86") => "linux-ia32", + ("linux", "powerpc64") => "linux-ppc64", + ("linux", "s390x") => "linux-s390x", + ("linux", "x86_64") => "linux-x64", + ("netbsd", "aarch64") => "netbsd-arm64", + ("netbsd", "x86_64") => "netbsd-x64", + ("openbsd", "aarch64") => "openbsd-arm64", + ("openbsd", "x86_64") => "openbsd-x64", + ("solaris", "x86_64") => "sunos-x64", + ("windows", "aarch64") => "win32-arm64", + ("windows", "x86") => "win32-ia32", + ("windows", "x86_64") => "win32-x64", _ => panic!("Platform unsupported by esbuild."), } } diff --git a/worker-build/src/js/glue.js b/worker-build/src/js/glue.js deleted file mode 100644 index 19078406..00000000 --- a/worker-build/src/js/glue.js +++ /dev/null @@ -1,5 +0,0 @@ -import wasmModule from "./index.wasm"; -import * as imports from "./index_bg.js"; - -const instance = new WebAssembly.Instance(wasmModule, { "./index_bg.js": imports }); -export default instance.exports; diff --git a/worker-build/src/js/shim-legacy.js b/worker-build/src/js/shim-legacy.js new file mode 100644 index 00000000..893a6a78 --- /dev/null +++ b/worker-build/src/js/shim-legacy.js @@ -0,0 +1,55 @@ +import * as imports from "./index_bg.js"; +export * from "./index_bg.js"; +import wasmModule from "./index.wasm"; +import { WorkerEntrypoint } from "cloudflare:workers"; +$SNIPPET_JS_IMPORTS + +const instance = new WebAssembly.Instance(wasmModule, { + "./index_bg.js": imports, + $SNIPPET_WASM_IMPORTS +}); + +imports.__wbg_set_wasm(instance.exports); + +// Run the worker's initialization function. +instance.exports.__wbindgen_start?.(); + +export { wasmModule }; + +class Entrypoint extends WorkerEntrypoint { + async fetch(request) { + let response = imports.fetch(request, this.env, this.ctx); + $WAIT_UNTIL_RESPONSE; + return await response; + } + + async queue(batch) { + return await imports.queue(batch, this.env, this.ctx); + } + + async scheduled(event) { + return await imports.scheduled(event, this.env, this.ctx); + } +} + +const EXCLUDE_EXPORT = [ + "IntoUnderlyingByteSource", + "IntoUnderlyingSink", + "IntoUnderlyingSource", + "MinifyConfig", + "PolishConfig", + "R2Range", + "RequestRedirect", + "fetch", + "queue", + "scheduled", + "getMemory", +]; + +Object.keys(imports).map((k) => { + if (!(EXCLUDE_EXPORT.includes(k) | k.startsWith("__"))) { + Entrypoint.prototype[k] = imports[k]; + } +}); + +export default Entrypoint; diff --git a/worker-build/src/js/shim.js b/worker-build/src/js/shim.js index 19d55198..8518cd3f 100644 --- a/worker-build/src/js/shim.js +++ b/worker-build/src/js/shim.js @@ -1,45 +1,113 @@ -import * as imports from "./index_bg.js"; -export * from "./index_bg.js"; -import wasmModule from "./index.wasm"; import { WorkerEntrypoint } from "cloudflare:workers"; +import * as exports from "./index.js"; -// Run the worker's initialization function. -imports.start?.(); +Error.stackTraceLimit = 100; -export { wasmModule }; +let criticalError = false; +function registerPanicHook() { + if (exports.setPanicHook) + exports.setPanicHook(function (message) { + const panicError = new Error("Rust panic: " + message); + console.error('Critical', panicError); + criticalError = true; + }); +} -class Entrypoint extends WorkerEntrypoint { - async fetch(request) { - return await imports.fetch(request, this.env, this.ctx) - } +registerPanicHook(); - async queue(batch) { - return await imports.queue(batch, this.env, this.ctx) - } +let instanceId = 0; +function checkReinitialize() { + if (criticalError) { + console.log("Reinitializing Wasm application"); + exports.__wbg_reset_state(); + criticalError = false; + registerPanicHook(); + instanceId++; + } +} - async scheduled(event) { - return await imports.scheduled(event, this.env, this.ctx) - } +addEventListener('error', (e) => { + handleMaybeCritical(e.error); +}); + +function handleMaybeCritical(e) { + if (e instanceof WebAssembly.RuntimeError) { + console.error('Critical', e); + criticalError = true; + } } -const EXCLUDE_EXPORT = [ - "IntoUnderlyingByteSource", - "IntoUnderlyingSink", - "IntoUnderlyingSource", - "MinifyConfig", - "PolishConfig", - "R2Range", - "RequestRedirect", - "fetch", - "queue", - "scheduled", - "getMemory" -]; - -Object.keys(imports).map(k => { - if (!(EXCLUDE_EXPORT.includes(k) | k.startsWith("__"))) { - Entrypoint.prototype[k] = imports[k]; +class Entrypoint extends WorkerEntrypoint {} + +$HANDLERS + +const instanceProxyHooks = { + set: (target, prop, value, receiver) => Reflect.set(target.instance, prop, value, receiver), + has: (target, prop) => Reflect.has(target.instance, prop), + deleteProperty: (target, prop) => Reflect.deleteProperty(target.instance, prop), + apply: (target, thisArg, args) => Reflect.apply(target.instance, thisArg, args), + construct: (target, args, newTarget) => Reflect.construct(target.instance, args, newTarget), + getPrototypeOf: (target) => Reflect.getPrototypeOf(target.instance), + setPrototypeOf: (target, proto) => Reflect.setPrototypeOf(target.instance, proto), + isExtensible: (target) => Reflect.isExtensible(target.instance), + preventExtensions: (target) => Reflect.preventExtensions(target.instance), + getOwnPropertyDescriptor: (target, prop) => Reflect.getOwnPropertyDescriptor(target.instance, prop), + defineProperty: (target, prop, descriptor) => Reflect.defineProperty(target.instance, prop, descriptor), + ownKeys: (target) => Reflect.ownKeys(target.instance), +}; + +const classProxyHooks = { + construct(ctor, args, newTarget) { + try { + checkReinitialize(); + const instance = { + instance: Reflect.construct(ctor, args, newTarget), + instanceId, + ctor, + args, + newTarget + }; + return new Proxy(instance, { + ...instanceProxyHooks, + get(target, prop, receiver) { + if (target.instanceId !== instanceId) { + target.instance = Reflect.construct(target.ctor, target.args, target.newTarget); + target.instanceId = instanceId; + } + const original = Reflect.get(target.instance, prop, receiver); + if (typeof original !== 'function') return original; + if (original.constructor === Function) { + return new Proxy(original, { + apply(target, thisArg, argArray) { + checkReinitialize(); + try { + return target.apply(thisArg, argArray); + } catch (e) { + handleMaybeCritical(e); + throw e; + } + } + }); + } else { + return new Proxy(original, { + async apply(target, thisArg, argArray) { + checkReinitialize(); + try { + return await target.apply(thisArg, argArray); + } catch (e) { + handleMaybeCritical(e); + throw e; + } + } + }); + } + } + }); + } catch (e) { + criticalError = true; + throw e; } -}) + } +}; -export default Entrypoint; \ No newline at end of file +export default new Proxy(Entrypoint, classProxyHooks); diff --git a/worker-build/src/main.rs b/worker-build/src/main.rs index 807f3694..10d457d9 100644 --- a/worker-build/src/main.rs +++ b/worker-build/src/main.rs @@ -1,7 +1,4 @@ -//! Arguments are forwarded directly to wasm-pack - use std::{ - convert::TryInto, env::{self, VarError}, fs::{self, File}, io::{Read, Write}, @@ -12,33 +9,82 @@ use std::{ use anyhow::Result; use clap::Parser; -use wasm_pack::command::build::{Build, BuildOptions}; const OUT_DIR: &str = "build"; -const OUT_NAME: &str = "index"; -const WORKER_SUBDIR: &str = "worker"; -const WASM_IMPORT: &str = r#"let wasm; -export function __wbg_set_wasm(val) { - wasm = val; -} +const SHIM_FILE: &str = include_str!("./js/shim.js"); -"#; +mod install; +mod main_legacy; +mod wasm_pack; + +use wasm_pack::command::build::{Build, BuildOptions}; -const WASM_IMPORT_REPLACEMENT: &str = r#" -import wasm from './glue.js'; +use crate::wasm_pack::command::build::Target; -export function getMemory() { - return wasm.memory; +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +fn fix_wasm_import() -> Result<()> { + let index_path = output_path("index.js"); + let content = fs::read_to_string(&index_path)?; + let updated_content = content.replace("import source ", "import "); + fs::write(&index_path, updated_content)?; + Ok(()) } -"#; -mod install; +fn update_package_json() -> Result<()> { + let package_json_path = output_path("package.json"); + + let original_content = fs::read_to_string(&package_json_path)?; + let mut package_json: serde_json::Value = serde_json::from_str(&original_content)?; + + package_json["files"] = serde_json::json!(["index_bg.wasm", "index.js", "index.d.ts"]); + package_json["main"] = serde_json::Value::String("index.js".to_string()); + package_json["sideEffects"] = serde_json::json!(["./index.js"]); + + let updated_content = serde_json::to_string_pretty(&package_json)?; + fs::write(package_json_path, updated_content)?; + Ok(()) +} pub fn main() -> Result<()> { - // Our tests build the bundle ourselves. - if !cfg!(test) { - wasm_pack_build(env::args().skip(1))?; + let args: Vec<_> = env::args().collect(); + if matches!( + args.first().map(String::as_str), + Some("--version") | Some("-v") + ) { + println!("{}", VERSION); + return Ok(()); + } + let no_panic_recovery = args.iter().any(|a| a == "--no-panic-recovery"); + + let out_path = output_path(""); + if out_path.exists() { + fs::remove_dir_all(out_path)?; + } + + let wasm_pack_opts = parse_wasm_pack_opts(env::args().skip(1))?; + let mut wasm_pack_build = Build::try_from_opts(wasm_pack_opts)?; + + wasm_pack_build.init()?; + + let supports_reset_state = wasm_pack_build.supports_target_module_and_reset_state()?; + let module_target = + supports_reset_state && !no_panic_recovery && env::var("CUSTOM_SHIM").is_err(); + if module_target { + wasm_pack_build + .extra_args + .push("--experimental-reset-state-function".to_string()); + wasm_pack_build.run()?; + } else { + if supports_reset_state { + // Enable once we have DO bindings to offer an alternative + // eprintln!("Using CUSTOM_SHIM will be deprecated in a future release."); + } else { + eprintln!("A newer version of wasm-bindgen is available. Update to use the latest workers-rs features."); + } + wasm_pack_build.target = Target::Bundler; + wasm_pack_build.run()?; } let with_coredump = env::var("COREDUMP").is_ok(); @@ -47,19 +93,113 @@ pub fn main() -> Result<()> { wasm_coredump()?; } - let esbuild_path = install::ensure_esbuild()?; + if module_target { + let shim = SHIM_FILE.replace("$HANDLERS", &generate_handlers()?); + fs::write(output_path("shim.js"), shim)?; - create_worker_dir()?; - copy_generated_code_to_worker_dir()?; - use_glue_import()?; + add_export_wrappers()?; - write_string_to_file(worker_path("glue.js"), include_str!("./js/glue.js"))?; - write_string_to_file(worker_path("shim.js"), include_str!("./js/shim.js"))?; + update_package_json()?; - bundle(&esbuild_path)?; + let esbuild_path = install::ensure_esbuild()?; + bundle(&esbuild_path)?; - remove_unused_js()?; + fix_wasm_import()?; + remove_unused_files()?; + + create_wrapper_alias(false)?; + } else { + main_legacy::process()?; + create_wrapper_alias(true)?; + } + + Ok(()) +} + +fn generate_handlers() -> Result { + let index_path = output_path("index.js"); + let content = fs::read_to_string(&index_path)?; + + // Extract ESM function exports from the wasm-bindgen generated output. + // This code is specialized to what wasm-bindgen outputs for ESM and is therefore + // brittle to upstream changes. It is comprehensive to current output patterns though. + // TODO: Convert this to Wasm binary exports analysis for entry point detection instead. + let mut func_names = Vec::new(); + for line in content.lines() { + if let Some(rest) = line.strip_prefix("export function") { + if let Some(bracket_pos) = rest.find("(") { + let func_name = rest[..bracket_pos].trim(); + // strip the exported function (we re-wrap all handlers) + if !SYSTEM_FNS.contains(&func_name) { + func_names.push(func_name); + } + } + } else if let Some(rest) = line.strip_prefix("export {") { + if let Some(as_pos) = rest.find(" as ") { + let rest = &rest[as_pos + 4..]; + if let Some(brace_pos) = rest.find("}") { + let func_name = rest[..brace_pos].trim(); + if !SYSTEM_FNS.contains(&func_name) { + func_names.push(func_name); + } + } + } + } + } + + let mut handlers = String::new(); + for func_name in func_names { + if func_name == "fetch" && env::var("RUN_TO_COMPLETION").is_ok() { + handlers += &format!( + "Entrypoint.prototype.fetch = async function fetch(request) {{ + let response = exports.fetch(request, this.env, this.ctx); + this.ctx.waitUntil(response); + return response; +}} +" + ); + } else if func_name == "fetch" || func_name == "queue" || func_name == "scheduled" { + // TODO: Switch these over to https://github.com/wasm-bindgen/wasm-bindgen/pull/4757 + // once that lands. + handlers += &format!( + "Entrypoint.prototype.{func_name} = function {func_name} (arg) {{ + return exports.{func_name}.call(this, arg, this.env, this.ctx); +}} +" + ); + } else { + handlers += &format!("Entrypoint.prototype.{func_name} = exports.{func_name};\n"); + } + } + + Ok(handlers) +} + +static SYSTEM_FNS: &[&str] = &["__wbg_reset_state", "setPanicHook"]; + +fn add_export_wrappers() -> Result<()> { + let index_path = output_path("index.js"); + let content = fs::read_to_string(&index_path)?; + + let mut class_names = Vec::new(); + for line in content.lines() { + if let Some(rest) = line.strip_prefix("export class ") { + if let Some(brace_pos) = rest.find("{") { + let class_name = rest[..brace_pos].trim(); + class_names.push(class_name.to_string()); + } + } + } + + let shim_path = output_path("shim.js"); + let mut output = fs::read_to_string(&shim_path)?; + for class_name in class_names { + output.push_str(&format!( + "export const {class_name} = new Proxy(exports.{class_name}, classProxyHooks);\n" + )); + } + fs::write(&shim_path, output)?; Ok(()) } @@ -82,7 +222,7 @@ fn wasm_coredump() -> Result<()> { anyhow::anyhow!("failed to spawn wasm-coredump-rewriter: {err}\n\n{INSTALL_HELP}.") })?; - let input_filename = output_path("index_bg.wasm"); + let input_filename = output_path("index.wasm"); let input_bytes = { let mut input = File::open(input_filename.clone()) @@ -128,6 +268,34 @@ fn wasm_coredump() -> Result<()> { } } +fn create_wrapper_alias(legacy: bool) -> Result<()> { + let msg = if !legacy { + "// Use index.js directly, this file provided for backwards compat +// with former shim.mjs only. +" + } else { + "" + }; + let path = if !legacy { + "../index.js" + } else { + "./worker/shim.mjs" + }; + let shim_content = format!( + "{msg}export * from '{path}'; +export {{ default }} from '{path}'; +" + ); + + if !legacy { + fs::create_dir(output_path("worker"))?; + fs::write(output_path("worker/shim.mjs"), shim_content)?; + } else { + fs::write(output_path("index.js"), shim_content)?; + } + Ok(()) +} + #[derive(Parser)] struct BuildArgs { #[clap(flatten)] @@ -144,11 +312,11 @@ where let mut build_args = vec![ "--no-typescript".to_owned(), "--target".to_owned(), - "bundler".to_owned(), + "module".to_owned(), "--out-dir".to_owned(), OUT_DIR.to_owned(), "--out-name".to_owned(), - OUT_NAME.to_owned(), + "index".to_owned(), ]; build_args.extend(args); @@ -157,84 +325,21 @@ where Ok(command.build_options) } -fn wasm_pack_build(args: I) -> Result<()> -where - I: IntoIterator, -{ - let opts = parse_wasm_pack_opts(args)?; - - let mut build = Build::try_from_opts(opts)?; - - build.run() -} - -fn create_worker_dir() -> Result<()> { - // create a directory for our worker to live in - let worker_dir = PathBuf::from(OUT_DIR).join(WORKER_SUBDIR); - - // remove anything that already exists - if worker_dir.is_dir() { - fs::remove_dir_all(&worker_dir)? - } else if worker_dir.is_file() { - fs::remove_file(&worker_dir)? - }; - - // create an output dir - fs::create_dir(worker_dir)?; - - Ok(()) -} - -fn copy_generated_code_to_worker_dir() -> Result<()> { - let glue_src = output_path(format!("{OUT_NAME}_bg.js")); - let glue_dest = worker_path(format!("{OUT_NAME}_bg.js")); - - let wasm_src = output_path(format!("{OUT_NAME}_bg.wasm")); - let wasm_dest = worker_path(format!("{OUT_NAME}.wasm")); - - // wasm-bindgen supports adding arbitrary JavaScript for a library, so we need to move that as well. - // https://rustwasm.github.io/wasm-bindgen/reference/js-snippets.html - let snippets_src = output_path("snippets"); - let snippets_dest = worker_path("snippets"); - - for (src, dest) in [ - (glue_src, glue_dest), - (wasm_src, wasm_dest), - (snippets_src, snippets_dest), - ] { - if !src.exists() { - continue; - } - - fs::rename(src, dest)?; - } - - Ok(()) -} - -// Replaces the wasm import with an import that instantiates the WASM modules itself. -fn use_glue_import() -> Result<()> { - let bindgen_glue_path = worker_path(format!("{OUT_NAME}_bg.js")); - let old_bindgen_glue = read_file_to_string(&bindgen_glue_path)?; - let fixed_bindgen_glue = old_bindgen_glue.replace(WASM_IMPORT, WASM_IMPORT_REPLACEMENT); - write_string_to_file(bindgen_glue_path, fixed_bindgen_glue)?; - Ok(()) -} - // Bundles the snippets and worker-related code into a single file. fn bundle(esbuild_path: &Path) -> Result<()> { let no_minify = !matches!(env::var("NO_MINIFY"), Err(VarError::NotPresent)); - let path = PathBuf::from(OUT_DIR).join(WORKER_SUBDIR).canonicalize()?; + let path = PathBuf::from(OUT_DIR).canonicalize()?; let esbuild_path = esbuild_path.canonicalize()?; let mut command = Command::new(esbuild_path); command.args([ - "--external:./index.wasm", + "--external:./index_bg.wasm", "--external:cloudflare:sockets", "--external:cloudflare:workers", "--format=esm", "--bundle", "./shim.js", - "--outfile=shim.mjs", + "--outfile=index.js", + "--allow-overwrite", ]); if !no_minify { @@ -249,47 +354,16 @@ fn bundle(esbuild_path: &Path) -> Result<()> { } } -// After bundling there's no reason why we'd want to upload our now un-used JavaScript so we'll -// delete it. -fn remove_unused_js() -> Result<()> { - let snippets_dir = worker_path("snippets"); - - if snippets_dir.exists() { - std::fs::remove_dir_all(&snippets_dir)?; +fn remove_unused_files() -> Result<()> { + std::fs::remove_file(output_path("index_bg.wasm.d.ts"))?; + std::fs::remove_file(output_path("shim.js"))?; + let snippets_path = output_path("snippets"); + if snippets_path.exists() { + std::fs::remove_dir_all(snippets_path)?; } - - for to_remove in [ - format!("{OUT_NAME}_bg.js"), - "shim.js".into(), - "glue.js".into(), - ] { - std::fs::remove_file(worker_path(to_remove))?; - } - Ok(()) } -fn read_file_to_string>(path: P) -> Result { - let file_size = path.as_ref().metadata()?.len().try_into()?; - let mut file = File::open(path)?; - let mut buf = Vec::with_capacity(file_size); - file.read_to_end(&mut buf)?; - String::from_utf8(buf).map_err(anyhow::Error::from) -} - -fn write_string_to_file>(path: P, contents: impl AsRef) -> Result<()> { - let mut file = File::create(path)?; - file.write_all(contents.as_ref().as_bytes())?; - - Ok(()) -} - -pub fn worker_path(name: impl AsRef) -> PathBuf { - PathBuf::from(OUT_DIR) - .join(WORKER_SUBDIR) - .join(name.as_ref()) -} - pub fn output_path(name: impl AsRef) -> PathBuf { PathBuf::from(OUT_DIR).join(name.as_ref()) } @@ -309,6 +383,8 @@ mod test { let args = vec!["--weak-refs".to_owned()]; let result = parse_wasm_pack_opts(args).unwrap(); - assert!(result.weak_refs); + #[allow(deprecated)] + let weak_refs = result.weak_refs; + assert!(weak_refs); } } diff --git a/worker-build/src/main_legacy.rs b/worker-build/src/main_legacy.rs new file mode 100644 index 00000000..def6d576 --- /dev/null +++ b/worker-build/src/main_legacy.rs @@ -0,0 +1,212 @@ +//! Arguments are forwarded directly to wasm-pack + +use std::{ + env::{self, VarError}, + fmt::Write as _, + fs::{self, read_to_string, File}, + io::Write, + path::{Path, PathBuf}, + process::Command, +}; + +use anyhow::Result; + +const OUT_DIR: &str = "build"; +const OUT_NAME: &str = "index"; +const WORKER_SUBDIR: &str = "worker"; + +const SHIM_TEMPLATE: &str = include_str!("./js/shim-legacy.js"); + +use crate::install; + +pub fn process() -> Result<()> { + let esbuild_path = install::ensure_esbuild()?; + + create_worker_dir()?; + copy_generated_code_to_worker_dir()?; + + let shim_template = match env::var("CUSTOM_SHIM") { + Ok(path) => { + let path = Path::new(&path).to_owned(); + println!("Using custom shim from {}", path.display()); + // NOTE: we fail in case that file doesnt exist or something else happens + read_to_string(path)? + } + Err(_) => SHIM_TEMPLATE.to_owned(), + }; + + let wait_until_response = if env::var("RUN_TO_COMPLETION").is_ok() { + "this.ctx.waitUntil(response);" + } else { + "" + }; + + let snippets_dir = worker_path("snippets"); + let mut snippets = Vec::new(); + let mut counter = 0; + + // wasm-bindgen outputs snippets (https://rustwasm.github.io/wasm-bindgen/reference/js-snippets.html) + // into the snippets folder, so we recursively read what files were written here and set these up as + // explicit imports for Wasm instantiation. + fn get_snippets( + path: &Path, + path_string: String, + counter: &mut i32, + snippets: &mut Vec<(String, String)>, + ) -> Result<()> { + if path.is_dir() { + for entry in fs::read_dir(path)? { + let entry = entry?; + get_snippets( + &entry.path(), + format!("{}/{}", path_string, &entry.file_name().to_string_lossy()), + counter, + snippets, + )?; + } + } else if path.is_file() { + snippets.push((format!("snippets_{counter}"), path_string)); + *counter += 1; + } + Ok(()) + } + + get_snippets( + &snippets_dir, + "./snippets".to_string(), + &mut counter, + &mut snippets, + )?; + + let js_imports = snippets + .iter() + .fold(String::new(), |mut output, (name, path)| { + let _ = writeln!(output, "import * as {name} from \"{path}\";"); + output + }); + + let wasm_imports = snippets + .into_iter() + .fold(String::new(), |mut output, (name, path)| { + let _ = writeln!(output, "\"{path}\": {name},"); + output + }); + + let shim = shim_template + .replace("$WAIT_UNTIL_RESPONSE", wait_until_response) + .replace("$SNIPPET_JS_IMPORTS", &js_imports) + .replace("$SNIPPET_WASM_IMPORTS", &wasm_imports); + + write_string_to_file(worker_path("shim.js"), shim)?; + + bundle(&esbuild_path)?; + + remove_unused_js()?; + + Ok(()) +} + +fn create_worker_dir() -> Result<()> { + // create a directory for our worker to live in + let worker_dir = PathBuf::from(OUT_DIR).join(WORKER_SUBDIR); + + // remove anything that already exists + if worker_dir.is_dir() { + fs::remove_dir_all(&worker_dir)? + } else if worker_dir.is_file() { + fs::remove_file(&worker_dir)? + }; + + // create an output dir + fs::create_dir(worker_dir)?; + + Ok(()) +} + +fn copy_generated_code_to_worker_dir() -> Result<()> { + let glue_src = output_path(format!("{OUT_NAME}_bg.js")); + let glue_dest = worker_path(format!("{OUT_NAME}_bg.js")); + + let wasm_src = output_path(format!("{OUT_NAME}_bg.wasm")); + let wasm_dest = worker_path(format!("{OUT_NAME}.wasm")); + + // wasm-bindgen supports adding arbitrary JavaScript for a library, so we need to move that as well. + // https://rustwasm.github.io/wasm-bindgen/reference/js-snippets.html + let snippets_src = output_path("snippets"); + let snippets_dest = worker_path("snippets"); + + for (src, dest) in [ + (glue_src, glue_dest), + (wasm_src, wasm_dest), + (snippets_src, snippets_dest), + ] { + if !src.exists() { + continue; + } + + fs::rename(src, dest)?; + } + + Ok(()) +} + +// Bundles the snippets and worker-related code into a single file. +fn bundle(esbuild_path: &Path) -> Result<()> { + let no_minify = !matches!(env::var("NO_MINIFY"), Err(VarError::NotPresent)); + let path = PathBuf::from(OUT_DIR).join(WORKER_SUBDIR).canonicalize()?; + let esbuild_path = esbuild_path.canonicalize()?; + let mut command = Command::new(esbuild_path); + command.args([ + "--external:./index.wasm", + "--external:cloudflare:sockets", + "--external:cloudflare:workers", + "--format=esm", + "--bundle", + "./shim.js", + "--outfile=shim.mjs", + ]); + + if !no_minify { + command.arg("--minify"); + } + + let exit_status = command.current_dir(path).spawn()?.wait()?; + + match exit_status.success() { + true => Ok(()), + false => anyhow::bail!("esbuild exited with status {}", exit_status), + } +} + +// After bundling there's no reason why we'd want to upload our now un-used JavaScript so we'll +// delete it. +fn remove_unused_js() -> Result<()> { + let snippets_dir = worker_path("snippets"); + + if snippets_dir.exists() { + std::fs::remove_dir_all(&snippets_dir)?; + } + + std::fs::remove_file(worker_path(format!("{OUT_NAME}_bg.js")))?; + std::fs::remove_file(worker_path("shim.js"))?; + std::fs::remove_file(output_path("index.js"))?; + + Ok(()) +} + +fn write_string_to_file>(path: P, contents: impl AsRef) -> Result<()> { + let mut file = File::create(path)?; + file.write_all(contents.as_ref().as_bytes())?; + + Ok(()) +} + +pub fn worker_path(name: impl AsRef) -> PathBuf { + PathBuf::from(OUT_DIR) + .join(WORKER_SUBDIR) + .join(name.as_ref()) +} + +pub fn output_path(name: impl AsRef) -> PathBuf { + PathBuf::from(OUT_DIR).join(name.as_ref()) +} diff --git a/worker-build/src/wasm_pack/bindgen.rs b/worker-build/src/wasm_pack/bindgen.rs new file mode 100644 index 00000000..8c6ade8e --- /dev/null +++ b/worker-build/src/wasm_pack/bindgen.rs @@ -0,0 +1,91 @@ +//! Functionality related to running `wasm-bindgen`. + +use crate::wasm_pack::child; +use crate::wasm_pack::command::build::{BuildProfile, Target}; +use crate::wasm_pack::install::{self, Tool}; +use crate::wasm_pack::manifest::CrateData; +use anyhow::{Context, Result}; +use std::path::Path; +use std::process::Command; + +/// Run the `wasm-bindgen` CLI to generate bindings for the current crate's +/// `.wasm`. +#[allow(clippy::too_many_arguments)] +pub fn wasm_bindgen_build( + data: &CrateData, + install_status: &install::Status, + out_dir: &Path, + out_name: &Option, + disable_dts: bool, + target: Target, + profile: BuildProfile, + extra_args: &[String], + extra_options: &[String], +) -> Result<()> { + let profile_name = match profile.clone() { + BuildProfile::Release | BuildProfile::Profiling => "release", + BuildProfile::Dev => "debug", + BuildProfile::Custom(profile_name) => &profile_name.clone(), + }; + + let out_dir = out_dir.to_str().unwrap(); + + let target_directory = { + let mut has_target_dir_iter = extra_options.iter(); + has_target_dir_iter + .find(|&it| it == "--target-dir") + .and_then(|_| has_target_dir_iter.next()) + .map(Path::new) + .unwrap_or(data.target_directory()) + }; + + let wasm_path = target_directory + .join("wasm32-unknown-unknown") + .join(profile_name) + .join(data.crate_name()) + .with_extension("wasm"); + + let dts_arg = if disable_dts { + "--no-typescript" + } else { + "--typescript" + }; + let bindgen_path = install::get_tool_path(install_status, Tool::WasmBindgen)? + .binary(&Tool::WasmBindgen.to_string())?; + + let mut cmd = Command::new(&bindgen_path); + cmd.arg(&wasm_path) + .arg("--out-dir") + .arg(out_dir) + .arg(dts_arg); + + cmd.arg("--target").arg(target.to_string()); + + if let Some(value) = out_name { + cmd.arg("--out-name").arg(value); + } + + let profile = data.configured_profile(profile); + if profile.wasm_bindgen_debug_js_glue() { + cmd.arg("--debug"); + } + if !profile.wasm_bindgen_demangle_name_section() { + cmd.arg("--no-demangle"); + } + if profile.wasm_bindgen_dwarf_debug_info() { + cmd.arg("--keep-debug"); + } + if profile.wasm_bindgen_omit_default_module_path() { + cmd.arg("--omit-default-module-path"); + } + if profile.wasm_bindgen_split_linked_modules() { + cmd.arg("--split-linked-modules"); + } + + for arg in extra_args { + cmd.arg(arg); + } + + child::run(cmd, "wasm-bindgen").context("Running the wasm-bindgen CLI")?; + Ok(()) +} diff --git a/worker-build/src/wasm_pack/build/mod.rs b/worker-build/src/wasm_pack/build/mod.rs new file mode 100644 index 00000000..4820e153 --- /dev/null +++ b/worker-build/src/wasm_pack/build/mod.rs @@ -0,0 +1,113 @@ +//! Building a Rust crate into a `.wasm` binary. + +use crate::wasm_pack::child; +use crate::wasm_pack::command::build::BuildProfile; +use crate::wasm_pack::emoji; +use crate::wasm_pack::PBAR; +use anyhow::{anyhow, bail, Context, Result}; +use std::path::Path; +use std::process::Command; +use std::str; + +pub mod wasm_target; + +/// Ensure that `rustc` is present and that it is >= 1.30.0 +pub fn check_rustc_version() -> Result { + let local_minor_version = rustc_minor_version(); + match local_minor_version { + Some(mv) => { + if mv < 30 { + bail!( + "Your version of Rust, '1.{}', is not supported. Please install Rust version 1.30.0 or higher.", + mv + ) + } else { + Ok(mv.to_string()) + } + } + None => bail!("We can't figure out what your Rust version is- which means you might not have Rust installed. Please install Rust version 1.30.0 or higher."), + } +} + +// from https://github.com/alexcrichton/proc-macro2/blob/79e40a113b51836f33214c6d00228934b41bd4ad/build.rs#L44-L61 +fn rustc_minor_version() -> Option { + macro_rules! otry { + ($e:expr) => { + match $e { + Some(e) => e, + None => return None, + } + }; + } + let output = otry!(Command::new("rustc").arg("--version").output().ok()); + let version = otry!(str::from_utf8(&output.stdout).ok()); + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + otry!(pieces.next()).parse().ok() +} + +/// Run `cargo build` targetting `wasm32-unknown-unknown`. +pub fn cargo_build_wasm( + path: &Path, + profile: BuildProfile, + extra_options: &[String], +) -> Result<()> { + let msg = format!("{}Compiling to Wasm...", emoji::CYCLONE); + PBAR.info(&msg); + + let mut cmd = Command::new("cargo"); + cmd.current_dir(path).arg("build").arg("--lib"); + + if PBAR.quiet() { + cmd.arg("--quiet"); + } + + match profile { + BuildProfile::Profiling => { + // Once there are DWARF debug info consumers, force enable debug + // info, because builds that use the release cargo profile disables + // debug info. + // + // cmd.env("RUSTFLAGS", "-g"); + cmd.arg("--release"); + } + BuildProfile::Release => { + cmd.arg("--release"); + } + BuildProfile::Dev => { + // Plain cargo builds use the dev cargo profile, which includes + // debug info by default. + } + BuildProfile::Custom(arg) => { + cmd.arg("--profile").arg(arg); + } + } + + cmd.arg("--target").arg("wasm32-unknown-unknown"); + + // The `cargo` command is executed inside the directory at `path`, so relative paths set via extra options won't work. + // To remedy the situation, all detected paths are converted to absolute paths. + let mut handle_path = false; + let extra_options_with_absolute_paths = extra_options + .iter() + .map(|option| -> Result { + let value = if handle_path && Path::new(option).is_relative() { + std::env::current_dir()? + .join(option) + .to_str() + .ok_or_else(|| anyhow!("path contains non-UTF-8 characters"))? + .to_string() + } else { + option.to_string() + }; + handle_path = matches!(&**option, "--target-dir" | "--out-dir" | "--manifest-path"); + Ok(value) + }) + .collect::>>()?; + cmd.args(extra_options_with_absolute_paths); + + child::run(cmd, "cargo build").context("Compiling your crate to WebAssembly failed")?; + Ok(()) +} diff --git a/worker-build/src/wasm_pack/build/wasm_target.rs b/worker-build/src/wasm_pack/build/wasm_target.rs new file mode 100644 index 00000000..b85c485d --- /dev/null +++ b/worker-build/src/wasm_pack/build/wasm_target.rs @@ -0,0 +1,171 @@ +//! Checking for the wasm32 target + +use crate::wasm_pack::child; +use crate::wasm_pack::emoji; +use crate::wasm_pack::PBAR; +use anyhow::{anyhow, bail, Context, Result}; +use log::error; +use log::info; +use std::fmt; +use std::path::PathBuf; +use std::process::Command; + +struct Wasm32Check { + rustc_path: PathBuf, + sysroot: PathBuf, + found: bool, + is_rustup: bool, +} + +impl fmt::Display for Wasm32Check { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let target = "wasm32-unknown-unknown"; + + if !self.found { + let rustup_string = if self.is_rustup { + "It looks like Rustup is being used.".to_owned() + } else { + format!("It looks like Rustup is not being used. For non-Rustup setups, the {} target needs to be installed manually. See https://rustwasm.github.io/wasm-pack/book/prerequisites/non-rustup-setups.html on how to do this.", target) + }; + + writeln!( + f, + "{} target not found in sysroot: {:?}", + target, self.sysroot + ) + .and_then(|_| { + writeln!( + f, + "\nUsed rustc from the following path: {:?}", + self.rustc_path + ) + }) + .and_then(|_| writeln!(f, "{}", rustup_string)) + } else { + write!( + f, + "sysroot: {:?}, rustc path: {:?}, was found: {}, isRustup: {}", + self.sysroot, self.rustc_path, self.found, self.is_rustup + ) + } + } +} + +/// Ensure that `rustup` has the `wasm32-unknown-unknown` target installed for +/// current toolchain +pub fn check_for_wasm32_target() -> Result<()> { + let msg = format!("{}Checking for the Wasm target...", emoji::TARGET); + PBAR.info(&msg); + + // Check if wasm32 target is present, otherwise bail. + match check_wasm32_target() { + Ok(ref wasm32_check) if wasm32_check.found => Ok(()), + Ok(wasm32_check) => bail!("{}", wasm32_check), + Err(err) => Err(err), + } +} + +/// Get rustc's sysroot as a PathBuf +fn get_rustc_sysroot() -> Result { + let command = Command::new("rustc") + .args(["--print", "sysroot"]) + .output()?; + + if command.status.success() { + Ok(String::from_utf8(command.stdout)?.trim().into()) + } else { + Err(anyhow!( + "Getting rustc's sysroot wasn't successful. Got {}", + command.status + )) + } +} + +/// Get wasm32-unknown-unknown target libdir +fn get_rustc_wasm32_unknown_unknown_target_libdir() -> Result { + let command = Command::new("rustc") + .args([ + "--target", + "wasm32-unknown-unknown", + "--print", + "target-libdir", + ]) + .output()?; + + if command.status.success() { + Ok(String::from_utf8(command.stdout)?.trim().into()) + } else { + Err(anyhow!( + "Getting rustc's wasm32-unknown-unknown target wasn't successful. Got {}", + command.status + )) + } +} + +fn does_wasm32_target_libdir_exist() -> bool { + let result = get_rustc_wasm32_unknown_unknown_target_libdir(); + + match result { + Ok(wasm32_target_libdir_path) => { + if wasm32_target_libdir_path.exists() { + info!( + "Found wasm32-unknown-unknown in {:?}", + wasm32_target_libdir_path + ); + true + } else { + info!( + "Failed to find wasm32-unknown-unknown in {:?}", + wasm32_target_libdir_path + ); + false + } + } + Err(_) => { + error!("Some error in getting the target libdir!"); + false + } + } +} + +fn check_wasm32_target() -> Result { + let sysroot = get_rustc_sysroot()?; + let rustc_path = which::which("rustc")?; + + if does_wasm32_target_libdir_exist() { + Ok(Wasm32Check { + rustc_path, + sysroot, + found: true, + is_rustup: false, + }) + // If it doesn't exist, then we need to check if we're using rustup. + } else { + // If sysroot contains "rustup", then we can assume we're using rustup + // and use rustup to add the wasm32-unknown-unknown target. + if sysroot.to_string_lossy().contains("rustup") { + rustup_add_wasm_target().map(|()| Wasm32Check { + rustc_path, + sysroot, + found: true, + is_rustup: true, + }) + } else { + Ok(Wasm32Check { + rustc_path, + sysroot, + found: false, + is_rustup: false, + }) + } + } +} + +/// Add wasm32-unknown-unknown using `rustup`. +fn rustup_add_wasm_target() -> Result<()> { + let mut cmd = Command::new("rustup"); + cmd.arg("target").arg("add").arg("wasm32-unknown-unknown"); + child::run(cmd, "rustup").context("Adding the wasm32-unknown-unknown target with rustup")?; + + Ok(()) +} diff --git a/worker-build/src/wasm_pack/cache.rs b/worker-build/src/wasm_pack/cache.rs new file mode 100644 index 00000000..cbb07b77 --- /dev/null +++ b/worker-build/src/wasm_pack/cache.rs @@ -0,0 +1,15 @@ +//! Getting and configuring wasm-pack's binary cache. + +use anyhow::Result; +use binary_install::Cache; +use std::env; +use std::path::Path; + +/// Get wasm-pack's binary cache. +pub fn get_wasm_pack_cache() -> Result { + if let Ok(path) = env::var("WASM_PACK_CACHE") { + Ok(Cache::at(Path::new(&path))) + } else { + Cache::new("wasm-pack") + } +} diff --git a/worker-build/src/wasm_pack/child.rs b/worker-build/src/wasm_pack/child.rs new file mode 100644 index 00000000..75d0703e --- /dev/null +++ b/worker-build/src/wasm_pack/child.rs @@ -0,0 +1,48 @@ +//! Utilities for managing child processes. +//! +//! This module helps us ensure that all child processes that we spawn get +//! properly logged and their output is logged as well. + +use crate::wasm_pack::install::Tool; +use anyhow::{bail, Result}; +use log::info; +use std::process::{Command, Stdio}; + +/// Run the given command and return on success. +pub fn run(mut command: Command, command_name: &str) -> Result<()> { + info!("Running {:?}", command); + + let status = command.status()?; + + if status.success() { + Ok(()) + } else { + bail!( + "failed to execute `{}`: exited with {}\n full command: {:?}", + command_name, + status, + command, + ) + } +} + +/// Run the given command and return its stdout. +pub fn run_capture_stdout(mut command: Command, command_name: &Tool) -> Result { + info!("Running {:?}", command); + + let output = command + .stderr(Stdio::inherit()) + .stdin(Stdio::inherit()) + .output()?; + + if output.status.success() { + Ok(String::from_utf8_lossy(&output.stdout).into_owned()) + } else { + bail!( + "failed to execute `{}`: exited with {}\n full command: {:?}", + command_name, + output.status, + command, + ) + } +} diff --git a/worker-build/src/wasm_pack/command/build.rs b/worker-build/src/wasm_pack/command/build.rs new file mode 100644 index 00000000..1e2f3c9e --- /dev/null +++ b/worker-build/src/wasm_pack/command/build.rs @@ -0,0 +1,486 @@ +//! Implementation of the `wasm-pack build` command. + +use crate::wasm_pack::bindgen; +use crate::wasm_pack::build; +use crate::wasm_pack::cache; +use crate::wasm_pack::emoji; +use crate::wasm_pack::install::{self, InstallMode, Tool}; +use crate::wasm_pack::license; +use crate::wasm_pack::lockfile::Lockfile; +use crate::wasm_pack::manifest; +use crate::wasm_pack::readme; +use crate::wasm_pack::utils::{create_pkg_dir, get_crate_path}; +use crate::wasm_pack::wasm_opt; +use crate::wasm_pack::PBAR; +use anyhow::{anyhow, bail, Error, Result}; +use binary_install::Cache; +use clap::Args; +use log::info; +use path_clean::PathClean; +use std::fmt; +use std::path::PathBuf; +use std::str::FromStr; +use std::time::Instant; + +/// Everything required to configure and run the `wasm-pack build` command. +#[allow(missing_docs)] +pub struct Build { + pub crate_path: PathBuf, + pub crate_data: manifest::CrateData, + pub scope: Option, + pub disable_dts: bool, + pub target: Target, + pub no_pack: bool, + pub no_opt: bool, + pub profile: BuildProfile, + pub mode: InstallMode, + pub out_dir: PathBuf, + pub out_name: Option, + pub bindgen: Option, + pub cache: Cache, + pub extra_args: Vec, + pub extra_options: Vec, +} + +/// What sort of output we're going to be generating and flags we're invoking +/// `wasm-bindgen` with. +#[derive(Clone, Copy, Debug, Default)] +pub enum Target { + /// Default output mode or `--target bundler`, indicates output will be + /// used with a bundle in a later step. + #[default] + Bundler, + /// Correspond to `--target web` where the output is natively usable as an + /// ES module in a browser and the wasm is manually instantiated. + Web, + /// Correspond to `--target nodejs` where the output is natively usable as + /// a Node.js module loaded with `require`. + Nodejs, + /// Correspond to `--target no-modules` where the output is natively usable + /// in a browser but pollutes the global namespace and must be manually + /// instantiated. + NoModules, + /// Correspond to `--target deno` where the output is natively usable as + /// a Deno module loaded with `import`. + Deno, + /// Correspond to `--target module` where the output uses ES module syntax + /// with source phase imports for WebAssembly modules. + Module, +} + +impl fmt::Display for Target { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Target::Bundler => "bundler", + Target::Web => "web", + Target::Nodejs => "nodejs", + Target::NoModules => "no-modules", + Target::Deno => "deno", + Target::Module => "module", + }; + write!(f, "{}", s) + } +} + +impl FromStr for Target { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "bundler" | "browser" => Ok(Target::Bundler), + "web" => Ok(Target::Web), + "nodejs" => Ok(Target::Nodejs), + "no-modules" => Ok(Target::NoModules), + "deno" => Ok(Target::Deno), + "module" => Ok(Target::Module), + _ => bail!("Unknown target: {}", s), + } + } +} + +/// The build profile controls whether optimizations, debug info, and assertions +/// are enabled or disabled. +#[derive(Clone, Debug)] +pub enum BuildProfile { + /// Enable assertions and debug info. Disable optimizations. + Dev, + /// Enable optimizations. Disable assertions and debug info. + Release, + /// Enable optimizations and debug info. Disable assertions. + Profiling, + /// User-defined profile with --profile flag + Custom(String), +} + +/// Everything required to configure and run the `wasm-pack build` command. +#[derive(Debug, Args, Default)] +#[command(allow_hyphen_values = true, trailing_var_arg = true)] +pub struct BuildOptions { + /// The path to the Rust crate. If not set, searches up the path from the current directory. + #[clap()] + pub path: Option, + + /// The npm scope to use in package.json, if any. + #[clap(long = "scope", short = 's')] + pub scope: Option, + + #[clap(long = "mode", short = 'm', default_value = "normal")] + /// Sets steps to be run. [possible values: no-install, normal, force] + pub mode: InstallMode, + + #[clap(long = "no-typescript")] + /// By default a *.d.ts file is generated for the generated JS file, but + /// this flag will disable generating this TypeScript file. + pub disable_dts: bool, + + #[clap(long = "target", short = 't', default_value = "bundler")] + /// Sets the target environment. [possible values: bundler, nodejs, web, no-modules, deno, module] + pub target: Target, + + #[clap(long = "debug")] + /// Deprecated. Renamed to `--dev`. + pub debug: bool, + + #[clap(long = "dev")] + /// Create a development build. Enable debug info, and disable + /// optimizations. + pub dev: bool, + + #[clap(long = "release")] + /// Create a release build. Enable optimizations and disable debug info. + pub release: bool, + + #[clap(long = "profiling")] + /// Create a profiling build. Enable optimizations and debug info. + pub profiling: bool, + + #[clap(long = "profile")] + /// User-defined profile with --profile flag + pub profile: Option, + + #[clap(long = "out-dir", short = 'd', default_value = "pkg")] + /// Sets the output directory with a relative path. + pub out_dir: String, + + #[clap(long = "out-name")] + /// Sets the output file names. Defaults to package name. + pub out_name: Option, + + #[clap(long = "no-pack", alias = "no-package")] + /// Option to not generate a package.json + pub no_pack: bool, + + #[clap(long = "no-opt", alias = "no-optimization")] + /// Option to skip optimization with wasm-opt + pub no_opt: bool, + + /// List of extra options to pass to `cargo build` + pub extra_options: Vec, + + #[clap(long, hide = true)] + /// Pass-through for --no-panic-recovery + pub no_panic_recovery: bool, + + #[deprecated(note = "runtime-detected")] + #[allow(dead_code)] + #[clap(long = "weak-refs", hide = true)] + /// Enable usage of the JS weak references proposal. + pub weak_refs: bool, + + #[deprecated(note = "automatically inferred from the Wasm features")] + #[clap(long = "reference-types", hide = true)] + /// Enable usage of WebAssembly reference types. + pub reference_types: bool, +} + +type BuildStep = fn(&mut Build) -> Result<()>; + +impl Build { + /// Construct a build command from the given options. + pub fn try_from_opts(mut build_opts: BuildOptions) -> Result { + if let Some(path) = &build_opts.path { + if path.to_string_lossy().starts_with("--") { + let path = build_opts.path.take().unwrap(); + build_opts + .extra_options + .insert(0, path.to_string_lossy().into_owned()); + } + } + let crate_path = get_crate_path(build_opts.path)?; + let crate_data = manifest::CrateData::new(&crate_path, build_opts.out_name.clone())?; + let out_dir = crate_path.join(PathBuf::from(build_opts.out_dir)).clean(); + + let dev = build_opts.dev || build_opts.debug; + let profile = match ( + dev, + build_opts.release, + build_opts.profiling, + build_opts.profile, + ) { + (false, false, false, None) | (false, true, false, None) => BuildProfile::Release, + (true, false, false, None) => BuildProfile::Dev, + (false, false, true, None) => BuildProfile::Profiling, + (false, false, false, Some(profile)) => BuildProfile::Custom(profile), + // Unfortunately, `clap` doesn't expose clap's `conflicts_with` + // functionality yet, so we have to implement it ourselves. + _ => bail!("Can only supply one of the --dev, --release, --profiling, or --profile 'name' flags"), + }; + + Ok(Build { + crate_path, + crate_data, + scope: build_opts.scope, + disable_dts: build_opts.disable_dts, + target: build_opts.target, + no_pack: build_opts.no_pack, + no_opt: build_opts.no_opt, + profile, + mode: build_opts.mode, + out_dir, + out_name: build_opts.out_name, + bindgen: None, + cache: cache::get_wasm_pack_cache()?, + extra_args: Vec::new(), + extra_options: build_opts.extra_options, + }) + } + + /// Prepare this `Build` command. + pub fn init(&mut self) -> Result<()> { + let process_steps = Build::get_preprocess_steps(self.mode); + for (_, process_step) in process_steps { + process_step(self)?; + } + Ok(()) + } + + /// Execute this `Build` command. + pub fn run(&mut self) -> Result<()> { + let process_steps = Build::get_process_steps(self.no_pack, self.no_opt); + + let started = Instant::now(); + + for (_, process_step) in process_steps { + process_step(self)?; + } + + let duration = crate::wasm_pack::utils::elapsed(started.elapsed()); + info!("Done in {}.", &duration); + info!( + "Your wasm pkg is ready to publish at {}.", + self.out_dir.display() + ); + + PBAR.info(&format!("{} Done in {}", emoji::SPARKLE, &duration)); + + PBAR.info(&format!( + "{} Your wasm pkg is ready to publish at {}.", + emoji::PACKAGE, + self.out_dir.display() + )); + Ok(()) + } + + #[allow(clippy::vec_init_then_push)] + fn get_preprocess_steps(mode: InstallMode) -> Vec<(&'static str, BuildStep)> { + macro_rules! steps { + ($($name:ident),+) => { + { + let mut steps: Vec<(&'static str, BuildStep)> = Vec::new(); + $(steps.push((stringify!($name), Build::$name));)* + steps + } + }; + ($($name:ident,)*) => (steps![$($name),*]) + } + let mut steps = Vec::new(); + match &mode { + InstallMode::Force => {} + _ => { + steps.extend(steps![ + step_check_rustc_version, + step_check_crate_config, + step_check_for_wasm_target, + ]); + } + } + + steps.extend(steps![step_install_wasm_bindgen]); + steps + } + + #[allow(clippy::vec_init_then_push)] + fn get_process_steps(no_pack: bool, no_opt: bool) -> Vec<(&'static str, BuildStep)> { + macro_rules! steps { + ($($name:ident),+) => { + { + let mut steps: Vec<(&'static str, BuildStep)> = Vec::new(); + $(steps.push((stringify!($name), Build::$name));)* + steps + } + }; + ($($name:ident,)*) => (steps![$($name),*]) + } + let mut steps = Vec::new(); + steps.extend(steps![ + step_build_wasm, + step_create_dir, + step_run_wasm_bindgen, + ]); + + if !no_opt { + steps.extend(steps![step_run_wasm_opt]); + } + + if !no_pack { + steps.extend(steps![ + step_create_json, + step_copy_readme, + step_copy_license, + ]); + } + + steps + } + + fn step_check_rustc_version(&mut self) -> Result<()> { + info!("Checking rustc version..."); + let version = build::check_rustc_version()?; + let msg = format!("rustc version is {}.", version); + info!("{}", &msg); + Ok(()) + } + + fn step_check_crate_config(&mut self) -> Result<()> { + info!("Checking crate configuration..."); + self.crate_data.check_crate_config()?; + info!("Crate is correctly configured."); + Ok(()) + } + + fn step_check_for_wasm_target(&mut self) -> Result<()> { + info!("Checking for wasm-target..."); + build::wasm_target::check_for_wasm32_target()?; + info!("Checking for wasm-target was successful."); + Ok(()) + } + + fn step_build_wasm(&mut self) -> Result<()> { + info!("Building wasm..."); + build::cargo_build_wasm(&self.crate_path, self.profile.clone(), &self.extra_options)?; + + info!( + "wasm built at {:#?}.", + &self + .crate_path + .join("target") + .join("wasm32-unknown-unknown") + .join("release") + ); + Ok(()) + } + + fn step_create_dir(&mut self) -> Result<()> { + info!("Creating a pkg directory..."); + create_pkg_dir(&self.out_dir)?; + info!("Created a pkg directory at {:#?}.", &self.crate_path); + Ok(()) + } + + fn step_create_json(&mut self) -> Result<()> { + self.crate_data.write_package_json( + &self.out_dir, + &self.scope, + self.disable_dts, + self.target, + )?; + info!( + "Wrote a package.json at {:#?}.", + &self.out_dir.join("package.json") + ); + Ok(()) + } + + fn step_copy_readme(&mut self) -> Result<()> { + info!("Copying readme from crate..."); + readme::copy_from_crate(&self.crate_data, &self.crate_path, &self.out_dir)?; + info!("Copied readme from crate to {:#?}.", &self.out_dir); + Ok(()) + } + + fn step_copy_license(&mut self) -> Result<()> { + info!("Copying license from crate..."); + license::copy_from_crate(&self.crate_data, &self.crate_path, &self.out_dir)?; + info!("Copied license from crate to {:#?}.", &self.out_dir); + Ok(()) + } + + fn step_install_wasm_bindgen(&mut self) -> Result<()> { + info!("Identifying wasm-bindgen dependency..."); + let lockfile = Lockfile::new(&self.crate_data)?; + let bindgen_version = lockfile.require_wasm_bindgen()?; + info!("Installing wasm-bindgen-cli..."); + let bindgen = install::download_prebuilt_or_cargo_install( + Tool::WasmBindgen, + &self.cache, + bindgen_version, + self.mode.install_permitted(), + )?; + self.bindgen = Some(bindgen); + info!("Installing wasm-bindgen-cli was successful."); + Ok(()) + } + + fn step_run_wasm_bindgen(&mut self) -> Result<()> { + info!("Building the wasm bindings..."); + bindgen::wasm_bindgen_build( + &self.crate_data, + self.bindgen.as_ref().unwrap(), + &self.out_dir, + &self.out_name, + self.disable_dts, + self.target, + self.profile.clone(), + &self.extra_args, + &self.extra_options, + )?; + info!("wasm bindings were built at {:#?}.", &self.out_dir); + Ok(()) + } + + fn step_run_wasm_opt(&mut self) -> Result<()> { + let mut args = match self + .crate_data + .configured_profile(self.profile.clone()) + .wasm_opt_args() + { + Some(args) => args, + None => return Ok(()), + }; + args.push("--all-features".into()); + // Keep the Wasm names section + args.push("--debuginfo".into()); + info!("executing wasm-opt with {:?}", args); + wasm_opt::run( + &self.cache, + &self.out_dir, + &args, + self.mode.install_permitted(), + ).map_err(|e| { + anyhow!( + "{}\nTo disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.", e + ) + }) + } + + pub fn supports_target_module_and_reset_state(&self) -> Result { + let bindgen_path = + install::get_tool_path(self.bindgen.as_ref().unwrap(), Tool::WasmBindgen)? + .binary(&Tool::WasmBindgen.to_string())?; + let cli_version = semver::Version::parse(&install::get_cli_version( + &install::Tool::WasmBindgen, + &bindgen_path, + )?)?; + let expected_version = semver::Version::parse("0.2.102")?; + Ok(cli_version >= expected_version) + } +} diff --git a/worker-build/src/wasm_pack/command/mod.rs b/worker-build/src/wasm_pack/command/mod.rs new file mode 100644 index 00000000..c990fbf9 --- /dev/null +++ b/worker-build/src/wasm_pack/command/mod.rs @@ -0,0 +1,4 @@ +//! CLI command structures, parsing, and execution. +#![allow(clippy::redundant_closure)] + +pub mod build; diff --git a/worker-build/src/wasm_pack/emoji.rs b/worker-build/src/wasm_pack/emoji.rs new file mode 100644 index 00000000..c297dd85 --- /dev/null +++ b/worker-build/src/wasm_pack/emoji.rs @@ -0,0 +1,21 @@ +//! Emoji constants used by `wasm-pack`. +//! +//! For the woefully unfamiliar: +//! +//! > Emoji are ideograms and smileys used in electronic messages and web +//! > pages. Emoji exist in various genres, including facial expressions, common +//! > objects, places and types of weather, and animals. They are much like +//! > emoticons, but emoji are actual pictures instead of typographics. +//! +//! -- https://en.wikipedia.org/wiki/Emoji + +#![allow(missing_docs)] + +use console::Emoji; + +pub static TARGET: Emoji = Emoji("🎯 ", ""); +pub static CYCLONE: Emoji = Emoji("🌀 ", ""); +pub static DOWN_ARROW: Emoji = Emoji("⬇️ ", ""); +pub static SPARKLE: Emoji = Emoji("✨ ", ":-)"); +pub static PACKAGE: Emoji = Emoji("📦 ", ":-)"); +pub static WARN: Emoji = Emoji("⚠️ ", ":-)"); diff --git a/worker-build/src/wasm_pack/install/arch.rs b/worker-build/src/wasm_pack/install/arch.rs new file mode 100644 index 00000000..f89e2ed5 --- /dev/null +++ b/worker-build/src/wasm_pack/install/arch.rs @@ -0,0 +1,41 @@ +use anyhow::{bail, Result}; +use std::fmt; + +use crate::wasm_pack::target; + +/// An enum representing supported architectures +#[derive(Clone, PartialEq, Eq)] +pub enum Arch { + /// x86 64-bit + X86_64, + /// x86 32-bit + X86, + /// ARM 64-bit + AArch64, +} + +impl Arch { + /// Gets the current architecture + pub fn get() -> Result { + if target::x86_64 { + Ok(Arch::X86_64) + } else if target::x86 { + Ok(Arch::X86) + } else if target::aarch64 { + Ok(Arch::AArch64) + } else { + bail!("Unrecognized target!") + } + } +} + +impl fmt::Display for Arch { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Arch::X86_64 => "x86-64", + Arch::X86 => "x86", + Arch::AArch64 => "aarch64", + }; + write!(f, "{}", s) + } +} diff --git a/worker-build/src/wasm_pack/install/krate.rs b/worker-build/src/wasm_pack/install/krate.rs new file mode 100644 index 00000000..1c71577d --- /dev/null +++ b/worker-build/src/wasm_pack/install/krate.rs @@ -0,0 +1,36 @@ +use crate::wasm_pack::install::Tool; +use anyhow::Result; +use serde::Deserialize; +const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); + +#[derive(Debug, Deserialize)] +pub struct Krate { + pub max_version: String, +} + +#[derive(Debug, Deserialize)] +pub struct KrateResponse { + #[serde(rename = "crate")] + pub krate: Krate, +} + +impl Krate { + pub fn new(name: &Tool) -> Result { + let krate_address = format!("https://crates.io/api/v1/crates/%7B%7D", name); + let mut config = ureq::Agent::config_builder(); + if let Some(proxy) = ureq::Proxy::try_from_env() { + config = config.proxy(Some(proxy)); + } + let agent = config.build().new_agent(); + let mut res = agent + .get(&krate_address) + .header( + "user-agent", + &format!("wasm-pack/{}", VERSION.unwrap_or("unknown")), + ) + .call()?; + + let kr: KrateResponse = res.body_mut().read_json()?; + Ok(kr.krate) + } +} diff --git a/worker-build/src/wasm_pack/install/mod.rs b/worker-build/src/wasm_pack/install/mod.rs new file mode 100644 index 00000000..a7c19f62 --- /dev/null +++ b/worker-build/src/wasm_pack/install/mod.rs @@ -0,0 +1,303 @@ +//! Functionality related to installing prebuilt binaries and/or running cargo install. + +use self::krate::Krate; +use crate::wasm_pack::child; +use crate::wasm_pack::emoji; +use crate::wasm_pack::PBAR; +use anyhow::{anyhow, bail, Context, Result}; +use binary_install::{Cache, Download}; +use log::debug; +use log::{info, warn}; +use std::env; +use std::fs; +use std::path::Path; +use std::process::Command; +use which::which; + +mod arch; +mod krate; +mod mode; +mod os; +mod tool; +pub use self::arch::Arch; +pub use self::mode::InstallMode; +pub use self::os::Os; +pub use self::tool::Tool; + +/// Possible outcomes of attempting to find/install a tool +pub enum Status { + /// Couldn't install tool because downloads are forbidden by user + CannotInstall, + /// We found the tool at the specified path + Found(Download), +} + +/// Handles possible installs status and returns the download or a error message +pub fn get_tool_path(status: &Status, tool: Tool) -> Result<&Download> { + match status { + Status::Found(download) => Ok(download), + Status::CannotInstall => bail!("Not able to find or install a local {}.", tool), + } +} + +/// Install a cargo CLI tool +/// +/// Prefers an existing local install, if any exists. Then checks if there is a +/// global install on `$PATH` that fits the bill. Then attempts to download a +/// tarball from the GitHub releases page, if this target has prebuilt +/// binaries. Finally, falls back to `cargo install`. +pub fn download_prebuilt_or_cargo_install( + tool: Tool, + cache: &Cache, + version: &str, + install_permitted: bool, +) -> Result { + // Check for custom wasm-bindgen path via environment variable + if matches!(tool, Tool::WasmBindgen) { + if let Ok(custom_path) = env::var("WASM_BINDGEN_PATH") { + eprintln!("Using custom wasm-bindgen path: {custom_path}"); + let path = Path::new(&custom_path); + if path.exists() { + info!( + "Using custom wasm-bindgen from WASM_BINDGEN_PATH: {}", + custom_path + ); + let download = Download::at(path.parent().unwrap_or(path)); + return Ok(Status::Found(download)); + } else { + warn!( + "WASM_BINDGEN_PATH set but path doesn't exist: {}", + custom_path + ); + } + } else { + eprintln!("Using global wasm-bindgen"); + } + } + + // If the tool is installed globally and it has the right version, use + // that. Assume that other tools are installed next to it. + // + // This situation can arise if the tool is already installed via + // `cargo install`, for example. + if let Ok(path) = which(tool.to_string()) { + debug!("found global {} binary at: {}", tool, path.display()); + if check_version(&tool, &path, version)? { + let download = Download::at(path.parent().unwrap()); + return Ok(Status::Found(download)); + } + } + + let msg = format!("{}Installing {}...", emoji::DOWN_ARROW, tool); + PBAR.info(&msg); + + let dl = download_prebuilt(&tool, cache, version, install_permitted); + match dl { + Ok(dl) => return Ok(dl), + Err(e) => { + warn!( + "could not download pre-built `{}`: {}. Falling back to `cargo install`.", + tool, e + ); + } + } + + cargo_install(tool, cache, version, install_permitted) +} + +/// Check if the tool dependency is locally satisfied. +pub fn check_version(tool: &Tool, path: &Path, expected_version: &str) -> Result { + let expected_version = if expected_version == "latest" { + let krate = Krate::new(tool)?; + krate.max_version + } else { + expected_version.to_string() + }; + + let v = get_cli_version(tool, path)?; + info!( + "Checking installed `{}` version == expected version: {} == {}", + tool, v, &expected_version + ); + Ok(v == expected_version) +} + +/// Fetches the version of a CLI tool +pub fn get_cli_version(tool: &Tool, path: &Path) -> Result { + let mut cmd = Command::new(path); + cmd.arg("--version"); + let stdout = child::run_capture_stdout(cmd, tool)?; + let version = stdout.split_whitespace().nth(1); + match version { + Some(v) => Ok(v.to_string()), + None => bail!("Something went wrong! We couldn't determine your version of the wasm-bindgen CLI. We were supposed to set that up for you, so it's likely not your fault! You should file an issue: https://github.com/rustwasm/wasm-pack/issues/new?template=bug_report.md.") + } +} + +/// Downloads a precompiled copy of the tool, if available. +pub fn download_prebuilt( + tool: &Tool, + cache: &Cache, + version: &str, + install_permitted: bool, +) -> Result { + let url = match prebuilt_url(https://github.com/tool,%20version) { + Ok(url) => url, + Err(e) => bail!( + "no prebuilt {} binaries are available for this platform: {}", + tool, + e, + ), + }; + match tool { + Tool::WasmBindgen => { + let binaries = &["wasm-bindgen", "wasm-bindgen-test-runner"]; + match cache.download(install_permitted, "wasm-bindgen", binaries, &url)? { + Some(download) => Ok(Status::Found(download)), + None => bail!("wasm-bindgen v{} is not installed!", version), + } + } + Tool::WasmOpt => { + let binaries: &[&str] = match Os::get()? { + Os::MacOS => &["bin/wasm-opt", "lib/libbinaryen.dylib"], + Os::Linux => &["bin/wasm-opt"], + Os::Windows => &["bin/wasm-opt.exe"], + }; + match cache.download(install_permitted, "wasm-opt", binaries, &url)? { + Some(download) => Ok(Status::Found(download)), + // TODO(ag_dubs): why is this different? i forget... + None => Ok(Status::CannotInstall), + } + } + } +} + +/// Returns the URL of a precompiled version of wasm-bindgen, if we have one +/// available for our host platform. +fn prebuilt_url(/service/tool: &Tool, version: &str) -> Result { + let os = Os::get()?; + let arch = Arch::get()?; + prebuilt_url_for(tool, version, &arch, &os) +} + +/// Get the download URL for some tool at some version, architecture and operating system +pub fn prebuilt_url_for(tool: &Tool, version: &str, arch: &Arch, os: &Os) -> Result { + let target = match (os, arch, tool) { + (Os::Linux, Arch::AArch64, Tool::WasmOpt) => "aarch64-linux", + (Os::Linux, Arch::AArch64, _) => "aarch64-unknown-linux-gnu", + (Os::Linux, Arch::X86_64, Tool::WasmOpt) => "x86_64-linux", + (Os::Linux, Arch::X86_64, _) => "x86_64-unknown-linux-musl", + (Os::MacOS, Arch::X86_64, Tool::WasmOpt) => "x86_64-macos", + (Os::MacOS, Arch::X86_64, _) => "x86_64-apple-darwin", + (Os::MacOS, Arch::AArch64, Tool::WasmOpt) => "arm64-macos", + (Os::Windows, Arch::X86_64, Tool::WasmOpt) => "x86_64-windows", + (Os::Windows, Arch::X86_64, _) => "x86_64-pc-windows-msvc", + _ => bail!("Unrecognized target!"), + }; + match tool { + Tool::WasmBindgen => { + Ok(format!( + "https://github.com/wasm-bindgen/wasm-bindgen/releases/download/%7B0%7D/wasm-bindgen-%7B0%7D-%7B1%7D.tar.gz", + version, + target + )) + }, + Tool::WasmOpt => { + Ok(format!( + "https://github.com/WebAssembly/binaryen/releases/download/%7Bvers%7D/binaryen-%7Bvers%7D-%7Btarget%7D.tar.gz", + vers = "version_123", // Make sure to update the version in docs/src/cargo-toml-configuration.md as well + target = target, + )) + } + } +} + +/// Use `cargo install` to install the tool locally into the given +/// crate. +pub fn cargo_install( + tool: Tool, + cache: &Cache, + version: &str, + install_permitted: bool, +) -> Result { + debug!( + "Attempting to use a `cargo install`ed version of `{}={}`", + tool, version, + ); + + let dirname = format!("{}-cargo-install-{}", tool, version); + let destination = cache.join(dirname.as_ref()); + if destination.exists() { + debug!( + "`cargo install`ed `{}={}` already exists at {}", + tool, + version, + destination.display() + ); + let download = Download::at(&destination); + return Ok(Status::Found(download)); + } + + if !install_permitted { + return Ok(Status::CannotInstall); + } + + // Run `cargo install` to a temporary location to handle ctrl-c gracefully + // and ensure we don't accidentally use stale files in the future + let tmp = cache.join(format!(".{}", dirname).as_ref()); + drop(fs::remove_dir_all(&tmp)); + debug!("cargo installing {} to tempdir: {}", tool, tmp.display(),); + + let context = format!("failed to create temp dir for `cargo install {}`", tool); + fs::create_dir_all(&tmp).context(context)?; + + let crate_name = match tool { + Tool::WasmBindgen => "wasm-bindgen-cli".to_string(), + _ => tool.to_string(), + }; + let mut cmd = Command::new("cargo"); + + cmd.arg("install") + .arg("--force") + .arg(crate_name) + .arg("--root") + .arg(&tmp); + + if version != "latest" { + cmd.arg("--version").arg(version); + } + + let context = format!("Installing {} with cargo", tool); + child::run(cmd, "cargo install").context(context)?; + + // `cargo install` will put the installed binaries in `$root/bin/*`, but we + // just want them in `$root/*` directly (which matches how the tarballs are + // laid out, and where the rest of our code expects them to be). So we do a + // little renaming here. + let binaries: Result> = match tool { + Tool::WasmBindgen => Ok(vec!["wasm-bindgen", "wasm-bindgen-test-runner"]), + Tool::WasmOpt => bail!("Cannot install wasm-opt with cargo."), + }; + + for b in binaries?.iter().cloned() { + let from = tmp + .join("bin") + .join(b) + .with_extension(env::consts::EXE_EXTENSION); + let to = tmp.join(from.file_name().unwrap()); + fs::rename(&from, &to).with_context(|| { + anyhow!( + "failed to move {} to {} for `cargo install`ed `{}`", + from.display(), + to.display(), + b + ) + })?; + } + + // Finally, move the `tmp` directory into our binary cache. + fs::rename(&tmp, &destination)?; + + let download = Download::at(&destination); + Ok(Status::Found(download)) +} diff --git a/worker-build/src/wasm_pack/install/mode.rs b/worker-build/src/wasm_pack/install/mode.rs new file mode 100644 index 00000000..e6c096a0 --- /dev/null +++ b/worker-build/src/wasm_pack/install/mode.rs @@ -0,0 +1,39 @@ +use anyhow::{bail, Error, Result}; +use std::str::FromStr; + +/// The `InstallMode` determines which mode of initialization we are running, and +/// what install steps we perform. +#[derive(Clone, Copy, Debug, Default)] +pub enum InstallMode { + /// Perform all the install steps. + #[default] + Normal, + /// Don't install tools like `wasm-bindgen`, just use the global + /// environment's existing versions to do builds. + Noinstall, + /// Skip the rustc version check + Force, +} + +impl FromStr for InstallMode { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "no-install" => Ok(InstallMode::Noinstall), + "normal" => Ok(InstallMode::Normal), + "force" => Ok(InstallMode::Force), + _ => bail!("Unknown build mode: {}", s), + } + } +} + +impl InstallMode { + /// Determines if installation is permitted during a function call based on --mode flag + pub fn install_permitted(self) -> bool { + match self { + InstallMode::Normal => true, + InstallMode::Force => true, + InstallMode::Noinstall => false, + } + } +} diff --git a/worker-build/src/wasm_pack/install/os.rs b/worker-build/src/wasm_pack/install/os.rs new file mode 100644 index 00000000..8afaf449 --- /dev/null +++ b/worker-build/src/wasm_pack/install/os.rs @@ -0,0 +1,41 @@ +use anyhow::{bail, Result}; +use std::fmt; + +use crate::wasm_pack::target; + +/// An enum representing supported operating systems +#[derive(Clone, PartialEq, Eq)] +pub enum Os { + /// Linux operating system + Linux, + /// Macos operating system + MacOS, + /// Windows operating system + Windows, +} + +impl Os { + /// Get the current operating system + pub fn get() -> Result { + if target::LINUX { + Ok(Os::Linux) + } else if target::MACOS { + Ok(Os::MacOS) + } else if target::WINDOWS { + Ok(Os::Windows) + } else { + bail!("Unrecognized target!") + } + } +} + +impl fmt::Display for Os { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Os::Linux => "linux", + Os::MacOS => "macOS", + Os::Windows => "windows", + }; + write!(f, "{}", s) + } +} diff --git a/worker-build/src/wasm_pack/install/tool.rs b/worker-build/src/wasm_pack/install/tool.rs new file mode 100644 index 00000000..e9c96ebe --- /dev/null +++ b/worker-build/src/wasm_pack/install/tool.rs @@ -0,0 +1,19 @@ +use std::fmt; + +/// Represents the set of CLI tools wasm-pack uses +pub enum Tool { + /// wasm-bindgen CLI tools + WasmBindgen, + /// wasm-opt CLI tool + WasmOpt, +} + +impl fmt::Display for Tool { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Tool::WasmBindgen => "wasm-bindgen", + Tool::WasmOpt => "wasm-opt", + }; + write!(f, "{}", s) + } +} diff --git a/worker-build/src/wasm_pack/license.rs b/worker-build/src/wasm_pack/license.rs new file mode 100644 index 00000000..29a3de61 --- /dev/null +++ b/worker-build/src/wasm_pack/license.rs @@ -0,0 +1,83 @@ +//! Copy `LICENSE` file(s) for the packaged wasm. + +use anyhow::{anyhow, Result}; +use std::fs; +use std::path::Path; + +use crate::wasm_pack::manifest::CrateData; +use crate::wasm_pack::PBAR; +use glob::glob; + +fn glob_license_files(path: &Path) -> Result> { + let mut license_files: Vec = Vec::new(); + let path_string = match path.join("LICENSE*").to_str() { + Some(path_string) => path_string.to_owned(), + None => { + return Err(anyhow!("Could not convert joined license path to String")); + } + }; + + for entry in glob(&path_string)? { + match entry { + Ok(globed_path) => { + let file_name = match globed_path.file_name() { + Some(file_name) => file_name, + None => return Err(anyhow!("Could not get file name from path")), + }; + let file_name_string = match file_name.to_str() { + Some(file_name_string) => file_name_string.to_owned(), + None => return Err(anyhow!("Could not convert filename to String")), + }; + license_files.push(file_name_string); + } + Err(e) => println!("{:?}", e), + } + } + Ok(license_files) +} + +/// Copy the crate's license into the `pkg` directory. +pub fn copy_from_crate(crate_data: &CrateData, path: &Path, out_dir: &Path) -> Result<()> { + assert!( + fs::metadata(path).ok().is_some_and(|m| m.is_dir()), + "crate directory should exist" + ); + + assert!( + fs::metadata(out_dir).ok().is_some_and(|m| m.is_dir()), + "crate's pkg directory should exist" + ); + + match (crate_data.crate_license(), crate_data.crate_license_file()) { + (Some(_), _) => { + let license_files = glob_license_files(path); + + match license_files { + Ok(files) => { + if files.is_empty() { + PBAR.info("License key is set in Cargo.toml but no LICENSE file(s) were found; Please add the LICENSE file(s) to your project directory"); + return Ok(()); + } + for license_file in files { + let crate_license_path = path.join(&license_file); + let new_license_path = out_dir.join(&license_file); + if fs::copy(&crate_license_path, &new_license_path).is_err() { + PBAR.info("origin crate has no LICENSE"); + } + } + } + Err(_) => PBAR.info("origin crate has no LICENSE"), + } + } + (None, Some(license_file)) => { + let crate_license_path = path.join(&license_file); + let new_license_path = out_dir.join(&license_file); + if fs::copy(&crate_license_path, &new_license_path).is_err() { + PBAR.info("origin crate has no LICENSE"); + } + } + (None, None) => {} + }; + + Ok(()) +} diff --git a/worker-build/src/wasm_pack/lockfile.rs b/worker-build/src/wasm_pack/lockfile.rs new file mode 100644 index 00000000..d0b50c4e --- /dev/null +++ b/worker-build/src/wasm_pack/lockfile.rs @@ -0,0 +1,74 @@ +//! Reading Cargo.lock lock file. + +#![allow(clippy::new_ret_no_self)] + +use std::fs; +use std::path::PathBuf; + +use crate::wasm_pack::manifest::CrateData; +use anyhow::{anyhow, bail, Context, Result}; +use console::style; +use serde::Deserialize; + +/// This struct represents the contents of `Cargo.lock`. +#[derive(Clone, Debug, Deserialize)] +pub struct Lockfile { + package: Vec, +} + +/// This struct represents a single package entry in `Cargo.lock` +#[derive(Clone, Debug, Deserialize)] +struct Package { + name: String, + version: String, +} + +impl Lockfile { + /// Read the `Cargo.lock` file for the crate at the given path. + pub fn new(crate_data: &CrateData) -> Result { + let lock_path = get_lockfile_path(crate_data)?; + let lockfile = fs::read_to_string(&lock_path) + .with_context(|| anyhow!("failed to read: {}", lock_path.display()))?; + let lockfile = toml::from_str(&lockfile) + .with_context(|| anyhow!("failed to parse: {}", lock_path.display()))?; + Ok(lockfile) + } + + /// Get the version of `wasm-bindgen` dependency used in the `Cargo.lock`. + pub fn wasm_bindgen_version(&self) -> Option<&str> { + self.get_package_version("wasm-bindgen") + } + + /// Like `wasm_bindgen_version`, except it returns an error instead of + /// `None`. + pub fn require_wasm_bindgen(&self) -> Result<&str> { + self.wasm_bindgen_version().ok_or_else(|| { + anyhow!( + "Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n\ + [dependencies]\n\ + wasm-bindgen = \"0.2\"", + style("wasm-bindgen").bold().dim(), + ) + }) + } + + fn get_package_version(&self, package: &str) -> Option<&str> { + self.package + .iter() + .find(|p| p.name == package) + .map(|p| &p.version[..]) + } +} + +/// Given the path to the crate that we are building, return a `PathBuf` +/// containing the location of the lock file, by finding the workspace root. +fn get_lockfile_path(crate_data: &CrateData) -> Result { + // Check that a lock file can be found in the directory. Return an error + // if it cannot, otherwise return the path buffer. + let lockfile_path = crate_data.workspace_root().join("Cargo.lock"); + if !lockfile_path.is_file() { + bail!("Could not find lockfile at {:?}", lockfile_path) + } else { + Ok(lockfile_path) + } +} diff --git a/worker-build/src/wasm_pack/manifest/mod.rs b/worker-build/src/wasm_pack/manifest/mod.rs new file mode 100644 index 00000000..8d499443 --- /dev/null +++ b/worker-build/src/wasm_pack/manifest/mod.rs @@ -0,0 +1,730 @@ +//! Reading and writing Cargo.toml and package.json manifests. + +#![allow( + clippy::new_ret_no_self, + clippy::needless_pass_by_value, + clippy::redundant_closure +)] + +use anyhow::{anyhow, bail, Context, Result}; +mod npm; + +use std::path::Path; +use std::{collections::HashMap, fs}; + +use self::npm::{ + repository::Repository, CommonJSPackage, ESModulesPackage, NoModulesPackage, NpmPackage, +}; +use crate::wasm_pack::command::build::{BuildProfile, Target}; +use crate::wasm_pack::PBAR; +use cargo_metadata::{CrateType, Metadata, TargetKind}; +use serde::{self, Deserialize}; +use std::collections::BTreeSet; +use strsim::levenshtein; + +const WASM_PACK_METADATA_KEY: &str = "package.metadata.wasm-pack"; + +/// Store for metadata learned about a crate +pub struct CrateData { + data: Metadata, + current_idx: usize, + manifest: CargoManifest, + out_name: Option, +} + +#[doc(hidden)] +#[derive(Deserialize)] +pub struct CargoManifest { + package: CargoPackage, +} + +#[derive(Deserialize)] +struct CargoPackage { + name: String, + + #[serde(default)] + metadata: CargoMetadata, +} + +#[derive(Default, Deserialize)] +struct CargoMetadata { + #[serde(default, rename = "wasm-pack")] + wasm_pack: CargoWasmPack, +} + +#[derive(Default, Deserialize)] +struct CargoWasmPack { + #[serde(default)] + profile: CargoWasmPackProfiles, +} + +#[derive(Deserialize)] +struct CargoWasmPackProfiles { + #[serde( + default = "CargoWasmPackProfile::default_dev", + deserialize_with = "CargoWasmPackProfile::deserialize_dev" + )] + dev: CargoWasmPackProfile, + + #[serde( + default = "CargoWasmPackProfile::default_release", + deserialize_with = "CargoWasmPackProfile::deserialize_release" + )] + release: CargoWasmPackProfile, + + #[serde( + default = "CargoWasmPackProfile::default_profiling", + deserialize_with = "CargoWasmPackProfile::deserialize_profiling" + )] + profiling: CargoWasmPackProfile, + + #[serde( + default = "CargoWasmPackProfile::default_custom", + deserialize_with = "CargoWasmPackProfile::deserialize_custom" + )] + custom: CargoWasmPackProfile, +} + +impl Default for CargoWasmPackProfiles { + fn default() -> CargoWasmPackProfiles { + CargoWasmPackProfiles { + dev: CargoWasmPackProfile::default_dev(), + release: CargoWasmPackProfile::default_release(), + profiling: CargoWasmPackProfile::default_profiling(), + custom: CargoWasmPackProfile::default_custom(), + } + } +} + +/// This is where configuration goes for wasm-bindgen, wasm-opt, wasm-snip, or +/// anything else that wasm-pack runs. +#[derive(Default, Deserialize)] +pub struct CargoWasmPackProfile { + #[serde(default, rename = "wasm-bindgen")] + wasm_bindgen: CargoWasmPackProfileWasmBindgen, + #[serde(default, rename = "wasm-opt")] + wasm_opt: Option, +} + +#[derive(Default, Deserialize)] +struct CargoWasmPackProfileWasmBindgen { + #[serde(default, rename = "debug-js-glue")] + debug_js_glue: Option, + + #[serde(default, rename = "demangle-name-section")] + demangle_name_section: Option, + + #[serde(default, rename = "dwarf-debug-info")] + dwarf_debug_info: Option, + + #[serde(default, rename = "omit-default-module-path")] + omit_default_module_path: Option, + + #[serde(default, rename = "split-linked-modules")] + split_linked_modules: Option, +} + +#[derive(Clone, Deserialize)] +#[serde(untagged)] +enum CargoWasmPackProfileWasmOpt { + Enabled(bool), + ExplicitArgs(Vec), +} + +impl Default for CargoWasmPackProfileWasmOpt { + fn default() -> Self { + CargoWasmPackProfileWasmOpt::Enabled(false) + } +} + +impl CargoWasmPackProfile { + fn default_dev() -> Self { + CargoWasmPackProfile { + wasm_bindgen: CargoWasmPackProfileWasmBindgen { + debug_js_glue: Some(true), + demangle_name_section: Some(true), + dwarf_debug_info: Some(false), + omit_default_module_path: Some(false), + split_linked_modules: Some(false), + }, + wasm_opt: None, + } + } + + fn default_release() -> Self { + CargoWasmPackProfile { + wasm_bindgen: CargoWasmPackProfileWasmBindgen { + debug_js_glue: Some(false), + demangle_name_section: Some(true), + dwarf_debug_info: Some(false), + omit_default_module_path: Some(false), + split_linked_modules: Some(false), + }, + wasm_opt: Some(CargoWasmPackProfileWasmOpt::Enabled(true)), + } + } + + fn default_profiling() -> Self { + CargoWasmPackProfile { + wasm_bindgen: CargoWasmPackProfileWasmBindgen { + debug_js_glue: Some(false), + demangle_name_section: Some(true), + dwarf_debug_info: Some(false), + omit_default_module_path: Some(false), + split_linked_modules: Some(false), + }, + wasm_opt: Some(CargoWasmPackProfileWasmOpt::Enabled(true)), + } + } + + fn default_custom() -> Self { + CargoWasmPackProfile { + wasm_bindgen: CargoWasmPackProfileWasmBindgen { + debug_js_glue: Some(false), + demangle_name_section: Some(true), + dwarf_debug_info: Some(false), + omit_default_module_path: Some(false), + split_linked_modules: Some(false), + }, + wasm_opt: Some(CargoWasmPackProfileWasmOpt::Enabled(true)), + } + } + + fn deserialize_dev<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut profile = >::deserialize(deserializer)?.unwrap_or_default(); + profile.update_with_defaults(&Self::default_dev()); + Ok(profile) + } + + fn deserialize_release<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut profile = >::deserialize(deserializer)?.unwrap_or_default(); + profile.update_with_defaults(&Self::default_release()); + Ok(profile) + } + + fn deserialize_profiling<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut profile = >::deserialize(deserializer)?.unwrap_or_default(); + profile.update_with_defaults(&Self::default_profiling()); + Ok(profile) + } + + fn deserialize_custom<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut profile = >::deserialize(deserializer)?.unwrap_or_default(); + profile.update_with_defaults(&Self::default_custom()); + Ok(profile) + } + + fn update_with_defaults(&mut self, defaults: &Self) { + macro_rules! d { + ( $( $path:ident ).* ) => { + self. $( $path ).* .get_or_insert(defaults. $( $path ).* .unwrap()); + } + } + d!(wasm_bindgen.debug_js_glue); + d!(wasm_bindgen.demangle_name_section); + d!(wasm_bindgen.dwarf_debug_info); + d!(wasm_bindgen.omit_default_module_path); + d!(wasm_bindgen.split_linked_modules); + + if self.wasm_opt.is_none() { + self.wasm_opt = defaults.wasm_opt.clone(); + } + } + + /// Get this profile's configured `[wasm-bindgen.debug-js-glue]` value. + pub fn wasm_bindgen_debug_js_glue(&self) -> bool { + self.wasm_bindgen.debug_js_glue.unwrap() + } + + /// Get this profile's configured `[wasm-bindgen.demangle-name-section]` value. + pub fn wasm_bindgen_demangle_name_section(&self) -> bool { + self.wasm_bindgen.demangle_name_section.unwrap() + } + + /// Get this profile's configured `[wasm-bindgen.dwarf-debug-info]` value. + pub fn wasm_bindgen_dwarf_debug_info(&self) -> bool { + self.wasm_bindgen.dwarf_debug_info.unwrap() + } + + /// Get this profile's configured `[wasm-bindgen.omit-default-module-path]` value. + pub fn wasm_bindgen_omit_default_module_path(&self) -> bool { + self.wasm_bindgen.omit_default_module_path.unwrap() + } + + /// Get this profile's configured `[wasm-bindgen.split-linked-modules]` value. + pub fn wasm_bindgen_split_linked_modules(&self) -> bool { + self.wasm_bindgen.split_linked_modules.unwrap() + } + + /// Get this profile's configured arguments for `wasm-opt`, if enabled. + pub fn wasm_opt_args(&self) -> Option> { + match self.wasm_opt.as_ref()? { + CargoWasmPackProfileWasmOpt::Enabled(false) => None, + CargoWasmPackProfileWasmOpt::Enabled(true) => Some(vec!["-O".to_string()]), + CargoWasmPackProfileWasmOpt::ExplicitArgs(s) => Some(s.clone()), + } + } +} + +struct NpmData { + name: String, + files: Vec, + dts_file: Option, + main: String, + homepage: Option, // https://docs.npmjs.com/files/package.json#homepage, + keywords: Option>, // https://docs.npmjs.com/files/package.json#keywords +} + +#[doc(hidden)] +pub struct ManifestAndUnsedKeys { + pub manifest: CargoManifest, + pub unused_keys: BTreeSet, +} + +impl CrateData { + /// Reads all metadata for the crate whose manifest is inside the directory + /// specified by `path`. + pub fn new(crate_path: &Path, out_name: Option) -> Result { + let manifest_path = crate_path.join("Cargo.toml"); + if !manifest_path.is_file() { + bail!( + "crate directory is missing a `Cargo.toml` file; is `{}` the \ + wrong directory?", + crate_path.display() + ) + } + + let data = cargo_metadata::MetadataCommand::new() + .manifest_path(&manifest_path) + .exec()?; + + let manifest_and_keys = CrateData::parse_crate_data(&manifest_path)?; + CrateData::warn_for_unused_keys(&manifest_and_keys); + + let manifest = manifest_and_keys.manifest; + let current_idx = data + .packages + .iter() + .position(|pkg| { + pkg.name.as_ref() == manifest.package.name + && CrateData::is_same_path(pkg.manifest_path.as_std_path(), &manifest_path) + }) + .ok_or_else(|| anyhow!("failed to find package in metadata"))?; + + Ok(CrateData { + data, + manifest, + current_idx, + out_name, + }) + } + + fn is_same_path(path1: &Path, path2: &Path) -> bool { + if let Ok(path1) = fs::canonicalize(path1) { + if let Ok(path2) = fs::canonicalize(path2) { + return path1 == path2; + } + } + path1 == path2 + } + + /// Read the `manifest_path` file and deserializes it using the toml Deserializer. + /// Returns a Result containing `ManifestAndUnsedKeys` which contains `CargoManifest` + /// and a `BTreeSet` containing the unused keys from the parsed file. + /// + /// # Errors + /// Will return Err if the file (manifest_path) couldn't be read or + /// if deserialize to `CargoManifest` fails. + pub fn parse_crate_data(manifest_path: &Path) -> Result { + let manifest = fs::read_to_string(manifest_path) + .with_context(|| anyhow!("failed to read: {}", manifest_path.display()))?; + let manifest = toml::Deserializer::parse(&manifest)?; + + let mut unused_keys = BTreeSet::new(); + let levenshtein_threshold = 1; + + let manifest: CargoManifest = serde_ignored::deserialize(manifest, |path| { + let path_string = path.to_string(); + + if path_string.starts_with("package.metadata") + && (path_string.contains("wasm-pack") + || levenshtein(WASM_PACK_METADATA_KEY, &path_string) <= levenshtein_threshold) + { + unused_keys.insert(path_string); + } + }) + .with_context(|| anyhow!("failed to parse manifest: {}", manifest_path.display()))?; + + Ok(ManifestAndUnsedKeys { + manifest, + unused_keys, + }) + } + + /// Iterating through all the passed `unused_keys` and output + /// a warning for each unknown key. + pub fn warn_for_unused_keys(manifest_and_keys: &ManifestAndUnsedKeys) { + manifest_and_keys.unused_keys.iter().for_each(|path| { + PBAR.warn(&format!( + "\"{}\" is an unknown key and will be ignored. Please check your Cargo.toml.", + path + )); + }); + } + + /// Get the configured profile. + pub fn configured_profile(&self, profile: BuildProfile) -> &CargoWasmPackProfile { + match profile { + BuildProfile::Dev => &self.manifest.package.metadata.wasm_pack.profile.dev, + BuildProfile::Profiling => &self.manifest.package.metadata.wasm_pack.profile.profiling, + BuildProfile::Release => &self.manifest.package.metadata.wasm_pack.profile.release, + BuildProfile::Custom(_) => &self.manifest.package.metadata.wasm_pack.profile.custom, + } + } + + /// Check that the crate the given path is properly configured. + pub fn check_crate_config(&self) -> Result<()> { + self.check_crate_type()?; + Ok(()) + } + + fn check_crate_type(&self) -> Result<()> { + let pkg = &self.data.packages[self.current_idx]; + let any_cdylib = pkg + .targets + .iter() + .filter(|target| target.kind.contains(&TargetKind::CDyLib)) + .any(|target| target.crate_types.contains(&CrateType::CDyLib)); + if any_cdylib { + return Ok(()); + } + bail!( + "crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your \ + Cargo.toml file:\n\n\ + [lib]\n\ + crate-type = [\"cdylib\", \"rlib\"]" + ) + } + + fn pkg(&self) -> &cargo_metadata::Package { + &self.data.packages[self.current_idx] + } + + /// Get the crate name for the crate at the given path. + pub fn crate_name(&self) -> String { + let pkg = self.pkg(); + match pkg + .targets + .iter() + .find(|t| t.kind.contains(&TargetKind::CDyLib)) + { + Some(lib) => lib.name.replace("-", "_"), + None => pkg.name.replace("-", "_"), + } + } + + /// Get the prefix for output file names + pub fn name_prefix(&self) -> String { + match &self.out_name { + Some(value) => value.clone(), + None => self.crate_name(), + } + } + + /// Gets the optional path to the readme, or None if disabled. + pub fn crate_readme(&self) -> Option { + self.pkg() + .readme + .clone() + .map(|readme_file| readme_file.into_string()) + } + + /// Get the license for the crate at the given path. + pub fn crate_license(&self) -> &Option { + &self.pkg().license + } + + /// Get the license file path for the crate at the given path. + pub fn crate_license_file(&self) -> Option { + self.pkg() + .license_file + .clone() + .map(|license_file| license_file.into_string()) + } + + /// Returns the path to this project's target directory where artifacts are + /// located after a cargo build. + pub fn target_directory(&self) -> &Path { + Path::new(&self.data.target_directory) + } + + /// Returns the path to this project's root cargo workspace directory + pub fn workspace_root(&self) -> &Path { + Path::new(&self.data.workspace_root) + } + + /// Generate a package.json file inside in `./pkg`. + pub fn write_package_json( + &self, + out_dir: &Path, + scope: &Option, + disable_dts: bool, + target: Target, + ) -> Result<()> { + let pkg_file_path = out_dir.join("package.json"); + // Check if a `package.json` was already generated by wasm-bindgen, if so + // we merge the NPM dependencies already specified in it. + let existing_deps = if pkg_file_path.exists() { + // It's just a map of dependency names to versions + Some(serde_json::from_str::>( + &fs::read_to_string(&pkg_file_path)?, + )?) + } else { + None + }; + let npm_data = match target { + Target::Nodejs => self.to_commonjs(scope, disable_dts, existing_deps, out_dir), + Target::NoModules => self.to_nomodules(scope, disable_dts, existing_deps, out_dir), + Target::Bundler => self.to_esmodules(scope, disable_dts, existing_deps, out_dir), + Target::Web => self.to_web(scope, disable_dts, existing_deps, out_dir), + Target::Module => self.to_esmodules(scope, disable_dts, existing_deps, out_dir), + // Deno does not need package.json + Target::Deno => return Ok(()), + }; + + let npm_json = serde_json::to_string_pretty(&npm_data)?; + + fs::write(&pkg_file_path, npm_json) + .with_context(|| anyhow!("failed to write: {}", pkg_file_path.display()))?; + Ok(()) + } + + fn npm_data( + &self, + scope: &Option, + add_js_bg_to_package_json: bool, + disable_dts: bool, + out_dir: &Path, + ) -> NpmData { + let name_prefix = self.name_prefix(); + let wasm_file = format!("{}_bg.wasm", name_prefix); + let js_file = format!("{}.js", name_prefix); + let mut files = vec![wasm_file]; + + files.push(js_file.clone()); + if add_js_bg_to_package_json { + let js_bg_file = format!("{}_bg.js", name_prefix); + files.push(js_bg_file); + } + + let pkg = &self.data.packages[self.current_idx]; + let npm_name = match scope { + Some(s) => format!("@{}/{}", s, pkg.name), + None => pkg.name.clone().as_ref().to_owned(), + }; + + let dts_file = if !disable_dts { + let file = format!("{}.d.ts", name_prefix); + files.push(file.to_string()); + Some(file) + } else { + None + }; + + let keywords = if !pkg.keywords.is_empty() { + Some(pkg.keywords.clone()) + } else { + None + }; + + if let Ok(entries) = fs::read_dir(out_dir) { + let file_names = entries + .filter_map(|e| e.ok()) + .filter(|e| e.metadata().map(|m| m.is_file()).unwrap_or(false)) + .filter_map(|e| e.file_name().into_string().ok()) + .filter(|f| f.starts_with("LICENSE")) + .filter(|f| f != "LICENSE"); + for file_name in file_names { + files.push(file_name); + } + } + + NpmData { + name: npm_name, + dts_file, + files, + main: js_file, + homepage: self.pkg().homepage.clone(), + keywords, + } + } + + fn license(&self) -> Option { + self.crate_license().clone().or_else(|| { + self.crate_license_file().clone().map(|file| { + // When license is written in file: https://docs.npmjs.com/files/package.json#license + format!("SEE LICENSE IN {}", file) + }) + }) + } + + fn to_commonjs( + &self, + scope: &Option, + disable_dts: bool, + dependencies: Option>, + out_dir: &Path, + ) -> NpmPackage { + let data = self.npm_data(scope, false, disable_dts, out_dir); + let pkg = &self.data.packages[self.current_idx]; + + self.check_optional_fields(); + + NpmPackage::CommonJSPackage(CommonJSPackage { + name: data.name, + collaborators: pkg.authors.clone(), + description: self.pkg().description.clone(), + version: pkg.version.to_string(), + license: self.license(), + repository: self.pkg().repository.clone().map(|repo_url| Repository { + ty: "git".to_string(), + url: repo_url, + }), + files: data.files, + main: data.main, + homepage: data.homepage, + types: data.dts_file, + keywords: data.keywords, + dependencies, + }) + } + + fn to_esmodules( + &self, + scope: &Option, + disable_dts: bool, + dependencies: Option>, + out_dir: &Path, + ) -> NpmPackage { + let data = self.npm_data(scope, true, disable_dts, out_dir); + let pkg = &self.data.packages[self.current_idx]; + + self.check_optional_fields(); + + NpmPackage::ESModulesPackage(ESModulesPackage { + name: data.name, + ty: "module".into(), + collaborators: pkg.authors.clone(), + description: self.pkg().description.clone(), + version: pkg.version.to_string(), + license: self.license(), + repository: self.pkg().repository.clone().map(|repo_url| Repository { + ty: "git".to_string(), + url: repo_url, + }), + files: data.files, + main: data.main.clone(), + homepage: data.homepage, + types: data.dts_file, + side_effects: vec![format!("./{}", data.main), "./snippets/*".to_owned()], + keywords: data.keywords, + dependencies, + }) + } + + fn to_web( + &self, + scope: &Option, + disable_dts: bool, + dependencies: Option>, + out_dir: &Path, + ) -> NpmPackage { + let data = self.npm_data(scope, false, disable_dts, out_dir); + let pkg = &self.data.packages[self.current_idx]; + + self.check_optional_fields(); + + NpmPackage::ESModulesPackage(ESModulesPackage { + name: data.name, + ty: "module".into(), + collaborators: pkg.authors.clone(), + description: self.pkg().description.clone(), + version: pkg.version.to_string(), + license: self.license(), + repository: self.pkg().repository.clone().map(|repo_url| Repository { + ty: "git".to_string(), + url: repo_url, + }), + files: data.files, + main: data.main, + homepage: data.homepage, + types: data.dts_file, + side_effects: vec!["./snippets/*".to_owned()], + keywords: data.keywords, + dependencies, + }) + } + + fn to_nomodules( + &self, + scope: &Option, + disable_dts: bool, + dependencies: Option>, + out_dir: &Path, + ) -> NpmPackage { + let data = self.npm_data(scope, false, disable_dts, out_dir); + let pkg = &self.data.packages[self.current_idx]; + + self.check_optional_fields(); + + NpmPackage::NoModulesPackage(NoModulesPackage { + name: data.name, + collaborators: pkg.authors.clone(), + description: self.pkg().description.clone(), + version: pkg.version.to_string(), + license: self.license(), + repository: self.pkg().repository.clone().map(|repo_url| Repository { + ty: "git".to_string(), + url: repo_url, + }), + files: data.files, + browser: data.main, + homepage: data.homepage, + types: data.dts_file, + keywords: data.keywords, + dependencies, + }) + } + + fn check_optional_fields(&self) { + let mut messages = vec![]; + if self.pkg().description.is_none() { + messages.push("description"); + } + if self.pkg().repository.is_none() { + messages.push("repository"); + } + if self.pkg().license.is_none() && self.pkg().license_file.is_none() { + messages.push("license"); + } + + match messages.len() { + 1 => PBAR.info(&format!("Optional field missing from Cargo.toml: '{}'. This is not necessary, but recommended", messages[0])), + 2 => PBAR.info(&format!("Optional fields missing from Cargo.toml: '{}', '{}'. These are not necessary, but recommended", messages[0], messages[1])), + 3 => PBAR.info(&format!("Optional fields missing from Cargo.toml: '{}', '{}', and '{}'. These are not necessary, but recommended", messages[0], messages[1], messages[2])), + _ => () + }; + } +} diff --git a/worker-build/src/wasm_pack/manifest/npm/commonjs.rs b/worker-build/src/wasm_pack/manifest/npm/commonjs.rs new file mode 100644 index 00000000..206dcfac --- /dev/null +++ b/worker-build/src/wasm_pack/manifest/npm/commonjs.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; + +use serde::Serialize; + +use crate::wasm_pack::manifest::npm::repository::Repository; + +#[derive(Serialize)] +pub struct CommonJSPackage { + pub name: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub collaborators: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + pub version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub license: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub repository: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub files: Vec, + pub main: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub homepage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub types: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub keywords: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub dependencies: Option>, +} diff --git a/worker-build/src/wasm_pack/manifest/npm/esmodules.rs b/worker-build/src/wasm_pack/manifest/npm/esmodules.rs new file mode 100644 index 00000000..a527fa9a --- /dev/null +++ b/worker-build/src/wasm_pack/manifest/npm/esmodules.rs @@ -0,0 +1,34 @@ +use std::collections::HashMap; + +use serde::Serialize; + +use crate::wasm_pack::manifest::npm::repository::Repository; + +#[derive(Serialize)] +pub struct ESModulesPackage { + pub name: String, + #[serde(rename = "type")] + pub ty: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub collaborators: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + pub version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub license: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub repository: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub files: Vec, + pub main: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub homepage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub types: Option, + #[serde(rename = "sideEffects")] + pub side_effects: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub keywords: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub dependencies: Option>, +} diff --git a/worker-build/src/wasm_pack/manifest/npm/mod.rs b/worker-build/src/wasm_pack/manifest/npm/mod.rs new file mode 100644 index 00000000..927feff0 --- /dev/null +++ b/worker-build/src/wasm_pack/manifest/npm/mod.rs @@ -0,0 +1,19 @@ +mod commonjs; +mod esmodules; +mod nomodules; +pub mod repository; + +use serde::Serialize; + +pub use self::commonjs::CommonJSPackage; +pub use self::esmodules::ESModulesPackage; +pub use self::nomodules::NoModulesPackage; + +#[derive(Serialize)] +#[serde(untagged)] +#[allow(clippy::enum_variant_names)] +pub enum NpmPackage { + CommonJSPackage(CommonJSPackage), + ESModulesPackage(ESModulesPackage), + NoModulesPackage(NoModulesPackage), +} diff --git a/worker-build/src/wasm_pack/manifest/npm/nomodules.rs b/worker-build/src/wasm_pack/manifest/npm/nomodules.rs new file mode 100644 index 00000000..8b6a20e3 --- /dev/null +++ b/worker-build/src/wasm_pack/manifest/npm/nomodules.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; + +use serde::Serialize; + +use crate::wasm_pack::manifest::npm::repository::Repository; + +#[derive(Serialize)] +pub struct NoModulesPackage { + pub name: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub collaborators: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + pub version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub license: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub repository: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub files: Vec, + pub browser: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub homepage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub types: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub keywords: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub dependencies: Option>, +} diff --git a/worker-build/src/wasm_pack/manifest/npm/repository.rs b/worker-build/src/wasm_pack/manifest/npm/repository.rs new file mode 100644 index 00000000..8fcd526c --- /dev/null +++ b/worker-build/src/wasm_pack/manifest/npm/repository.rs @@ -0,0 +1,8 @@ +use serde::Serialize; + +#[derive(Serialize)] +pub struct Repository { + #[serde(rename = "type")] + pub ty: String, + pub url: String, +} diff --git a/worker-build/src/wasm_pack/mod.rs b/worker-build/src/wasm_pack/mod.rs new file mode 100644 index 00000000..1ba9c67a --- /dev/null +++ b/worker-build/src/wasm_pack/mod.rs @@ -0,0 +1,34 @@ +//! Your favorite rust -> wasm workflow tool! + +#![deny(missing_docs)] + +extern crate anyhow; +extern crate cargo_metadata; +extern crate console; +extern crate glob; +extern crate parking_lot; +extern crate semver; +extern crate serde; +extern crate strsim; +extern crate which; + +pub(crate) mod bindgen; +pub(crate) mod build; +pub(crate) mod cache; +pub(crate) mod child; +pub(crate) mod command; +pub(crate) mod emoji; +pub(crate) mod install; +pub(crate) mod license; +pub(crate) mod lockfile; +pub(crate) mod manifest; +pub(crate) mod progressbar; +pub(crate) mod readme; +pub(crate) mod target; +pub(crate) mod utils; +pub(crate) mod wasm_opt; + +use crate::wasm_pack::progressbar::ProgressOutput; + +/// The global progress bar and user-facing message output. +pub(crate) static PBAR: ProgressOutput = ProgressOutput::new(); diff --git a/worker-build/src/wasm_pack/progressbar.rs b/worker-build/src/wasm_pack/progressbar.rs new file mode 100644 index 00000000..4bbbc2d2 --- /dev/null +++ b/worker-build/src/wasm_pack/progressbar.rs @@ -0,0 +1,90 @@ +//! Fancy progress bar functionality. + +use crate::wasm_pack::emoji; +use anyhow::{bail, Error, Result}; +use console::style; +use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +/// The maximum log level for wasm-pack +// The ordering is important: the least verbose must be at +// the top and the most verbose at the bottom +pub enum LogLevel { + /// Logs only error + Error, + /// Logs only warn and error + Warn, + /// Logs everything + Info, +} + +impl std::str::FromStr for LogLevel { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "error" => Ok(LogLevel::Error), + "warn" => Ok(LogLevel::Warn), + "info" => Ok(LogLevel::Info), + _ => bail!("Unknown log-level: {}", s), + } + } +} + +/// Synchronized progress bar and status message printing. +pub struct ProgressOutput { + quiet: AtomicBool, + log_level: AtomicU8, +} + +impl ProgressOutput { + /// Returns a new ProgressOutput + pub const fn new() -> Self { + Self { + quiet: AtomicBool::new(false), + log_level: AtomicU8::new(LogLevel::Info as u8), + } + } + + /// Print the given message. + fn message(&self, message: &str) { + eprintln!("{}", message); + } + + /// Returns whether it should silence stdout or not + pub fn quiet(&self) -> bool { + self.quiet.load(Ordering::SeqCst) + } + + /// Returns whether the specified log level is enabled or not + pub fn is_log_enabled(&self, level: LogLevel) -> bool { + (level as u8) <= self.log_level.load(Ordering::SeqCst) + } + + /// Add an informational message. + pub fn info(&self, message: &str) { + if !self.quiet() && self.is_log_enabled(LogLevel::Info) { + let info = format!("{}: {}", style("[INFO]").bold().dim(), message,); + self.message(&info); + } + } + + /// Add a warning message. + pub fn warn(&self, message: &str) { + if !self.quiet() && self.is_log_enabled(LogLevel::Warn) { + let warn = format!( + "{}: {} {}", + style("[WARN]").bold().dim(), + emoji::WARN, + message + ); + self.message(&warn); + } + } +} + +impl Default for ProgressOutput { + fn default() -> Self { + ProgressOutput::new() + } +} diff --git a/worker-build/src/wasm_pack/readme.rs b/worker-build/src/wasm_pack/readme.rs new file mode 100644 index 00000000..c8216b64 --- /dev/null +++ b/worker-build/src/wasm_pack/readme.rs @@ -0,0 +1,33 @@ +//! Generating `README` files for the packaged wasm. + +use anyhow::{Context, Result}; +use std::fs; +use std::path::Path; + +use crate::wasm_pack::manifest::CrateData; +use crate::wasm_pack::PBAR; + +/// Copy the crate's README into the `pkg` directory. +pub fn copy_from_crate(crate_data: &CrateData, path: &Path, out_dir: &Path) -> Result<()> { + assert!( + fs::metadata(path).ok().is_some_and(|m| m.is_dir()), + "crate directory should exist" + ); + assert!( + fs::metadata(out_dir).ok().is_some_and(|m| m.is_dir()), + "crate's pkg directory should exist" + ); + + let crate_readme_path = match crate_data.crate_readme() { + None => return Ok(()), + Some(readme_path) => path.join(readme_path), + }; + + let new_readme_path = out_dir.join("README.md"); + if crate_readme_path.exists() { + fs::copy(&crate_readme_path, &new_readme_path).context("failed to copy README")?; + } else { + PBAR.warn("origin crate has no README"); + } + Ok(()) +} diff --git a/worker-build/src/wasm_pack/target.rs b/worker-build/src/wasm_pack/target.rs new file mode 100644 index 00000000..0a6d38fb --- /dev/null +++ b/worker-build/src/wasm_pack/target.rs @@ -0,0 +1,17 @@ +//! Information about the target wasm-pack is currently being compiled for. +//! +//! That is, whether we are building wasm-pack for windows vs linux, and x86 vs +//! x86-64, etc. + +#![allow(missing_docs)] + +pub const LINUX: bool = cfg!(target_os = "linux"); +pub const MACOS: bool = cfg!(target_os = "macos"); +pub const WINDOWS: bool = cfg!(target_os = "windows"); + +#[allow(non_upper_case_globals)] +pub const x86_64: bool = cfg!(target_arch = "x86_64"); +#[allow(non_upper_case_globals)] +pub const x86: bool = cfg!(target_arch = "x86"); +#[allow(non_upper_case_globals)] +pub const aarch64: bool = cfg!(target_arch = "aarch64"); diff --git a/worker-build/src/wasm_pack/utils.rs b/worker-build/src/wasm_pack/utils.rs new file mode 100644 index 00000000..f21ae339 --- /dev/null +++ b/worker-build/src/wasm_pack/utils.rs @@ -0,0 +1,54 @@ +//! Utility functions for commands. +#![allow(clippy::redundant_closure)] + +use anyhow::Result; +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::Duration; + +/// If an explicit path is given, then use it, otherwise assume the current +/// directory is the crate path. +pub fn get_crate_path(path: Option) -> Result { + match path { + Some(p) => Ok(p), + None => find_manifest_from_cwd(), + } +} + +/// Search up the path for the manifest file from the current working directory +/// If we don't find the manifest file then return back the current working directory +/// to provide the appropriate error +fn find_manifest_from_cwd() -> Result { + let mut parent_path = std::env::current_dir()?; + let mut manifest_path = parent_path.join("Cargo.toml"); + loop { + if !manifest_path.is_file() { + if parent_path.pop() { + manifest_path = parent_path.join("Cargo.toml"); + } else { + return Ok(PathBuf::from(".")); + } + } else { + return Ok(parent_path); + } + } +} + +/// Construct our `pkg` directory in the crate. +pub fn create_pkg_dir(out_dir: &Path) -> Result<()> { + let _ = fs::remove_file(out_dir.join("package.json")); // Clean up package.json from previous runs + fs::create_dir_all(out_dir)?; + fs::write(out_dir.join(".gitignore"), "*")?; + Ok(()) +} + +/// Render a `Duration` to a form suitable for display on a console +pub fn elapsed(duration: Duration) -> String { + let secs = duration.as_secs(); + + if secs >= 60 { + format!("{}m {:02}s", secs / 60, secs % 60) + } else { + format!("{}.{:02}s", secs, duration.subsec_nanos() / 10_000_000) + } +} diff --git a/worker-build/src/wasm_pack/wasm_opt.rs b/worker-build/src/wasm_pack/wasm_opt.rs new file mode 100644 index 00000000..c78a6443 --- /dev/null +++ b/worker-build/src/wasm_pack/wasm_opt.rs @@ -0,0 +1,62 @@ +//! Support for downloading and executing `wasm-opt` + +use crate::wasm_pack::child; +use crate::wasm_pack::install; +use crate::wasm_pack::PBAR; +use anyhow::Result; +use binary_install::Cache; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; + +/// Execute `wasm-opt` over wasm binaries found in `out_dir`, downloading if +/// necessary into `cache`. Passes `args` to each invocation of `wasm-opt`. +pub fn run(cache: &Cache, out_dir: &Path, args: &[String], install_permitted: bool) -> Result<()> { + let wasm_opt_path = match find_wasm_opt(cache, install_permitted)? { + Some(path) => path, + // `find_wasm_opt` will have already logged a message about this, so we don't need to here. + None => return Ok(()), + }; + + PBAR.info("Optimizing wasm binaries with `wasm-opt`..."); + + for file in out_dir.read_dir()? { + let file = file?; + let path = file.path(); + if path.extension().and_then(|s| s.to_str()) != Some("wasm") { + continue; + } + + let tmp = path.with_extension("wasm-opt.wasm"); + let mut cmd = Command::new(&wasm_opt_path); + cmd.arg(&path).arg("-o").arg(&tmp).args(args); + child::run(cmd, "wasm-opt")?; + std::fs::rename(&tmp, &path)?; + } + + Ok(()) +} + +/// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a +/// precompiled binary. +/// +/// Returns `Some` if a binary was found or it was successfully downloaded. +/// Returns `None` if a binary wasn't found in `PATH` and this platform doesn't +/// have precompiled binaries. Returns an error if we failed to download the +/// binary. +pub fn find_wasm_opt(cache: &Cache, install_permitted: bool) -> Result> { + // We always ensure the guaranteed wasm-opt version instead of relying on PATH. + // First attempt to look up in PATH. If found assume it works. + // if let Ok(path) = which::which("wasm-opt") { + // PBAR.info(&format!("found wasm-opt at {:?}", path)); + // return Ok(Some(path)); + // } + + match install::download_prebuilt(&install::Tool::WasmOpt, cache, "latest", install_permitted)? { + install::Status::Found(download) => Ok(Some(download.binary("bin/wasm-opt")?)), + install::Status::CannotInstall => { + PBAR.info("Skipping wasm-opt as no downloading was requested"); + Ok(None) + } + } +} diff --git a/worker-codegen/Cargo.toml b/worker-codegen/Cargo.toml index 3eb1177f..5ffddf24 100644 --- a/worker-codegen/Cargo.toml +++ b/worker-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "worker-codegen" -version = "0.1.0" +version = "0.2.0" authors = ["Cloudflare Workers Team "] repository = "https://github.com/cloudflare/workers-rs/tree/main/worker-codegen" edition = "2018" @@ -17,10 +17,10 @@ path = "src/lib.rs" wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true wasm-bindgen-macro-support.workspace = true -syn = {version="2.0.17", features=['extra-traits']} +syn = { version = "2.0.17", features = ['extra-traits'] } proc-macro2 = "1.0.60" quote = "1.0.28" -prettyplease = { version="0.2" } -wit-parser= { version="0.205" } -convert_case={ version="0.6" } -anyhow={ version="1" } +prettyplease = { version = "0.2" } +wit-parser = { version = "0.205" } +convert_case = { version = "0.6" } +anyhow = { version = "1" } diff --git a/worker-codegen/src/wit.rs b/worker-codegen/src/wit.rs index 45142c39..50e2aa0a 100644 --- a/worker-codegen/src/wit.rs +++ b/worker-codegen/src/wit.rs @@ -29,7 +29,7 @@ fn wit_type_to_str(ty: &wit_parser::Type) -> anyhow::Result { wit_parser::Type::F64 => "f64".to_string(), wit_parser::Type::Char => "char".to_string(), wit_parser::Type::String => "String".to_string(), - wit_parser::Type::Id(t) => anyhow::bail!("Unsupported type: '{:?}'", t), + wit_parser::Type::Id(t) => anyhow::bail!("Unsupported type: '{t:?}'"), }) } @@ -91,10 +91,10 @@ fn expand_struct(struct_name: &Ident, sys_name: &Ident) -> anyhow::Result anyhow::Result { +fn expand_from_impl(struct_name: &Ident, from_type: &syn::Type) -> anyhow::Result { let impl_raw = quote!( - impl From<::worker::Fetcher> for #struct_name { - fn from(fetcher: ::worker::Fetcher) -> Self { + impl From<#from_type> for #struct_name { + fn from(fetcher: #from_type) -> Self { Self(::worker::send::SendWrapper::new(fetcher.into_rpc())) } } @@ -115,9 +115,9 @@ fn expand_rpc_impl( let mut impl_item: syn::ItemImpl = syn::parse2(impl_raw)?; for (name, method) in &interface.functions { - println!("\tFound method: '{}'.", name); + println!("\tFound method: '{name}'."); let ident = format_ident!("{}", name.to_case(Case::Snake)); - let invocation_raw = quote!(self.0.add()); + let invocation_raw = quote!(self.0.#ident()); let mut invocation_item: syn::ExprMethodCall = syn::parse2(invocation_raw)?; for (arg_name, _) in &method.params { let mut segments = syn::punctuated::Punctuated::new(); @@ -204,9 +204,9 @@ fn expand_wit(path: &str) -> anyhow::Result { for (_, interface) in resolver.interfaces { let name = interface.name.clone().unwrap(); - println!("Found Interface: '{}'", name); + println!("Found Interface: '{name}'"); let interface_name = format_ident!("{}", name.to_case(Case::Pascal)); - println!("Generating Trait '{}'", interface_name); + println!("Generating Trait '{interface_name}'"); let struct_name = format_ident!("{}Service", interface_name); let sys_name = format_ident!("{}Sys", interface_name); @@ -222,8 +222,15 @@ fn expand_wit(path: &str) -> anyhow::Result { &interface_name, &struct_name, )?)); - // From Impl - items.push(syn::Item::Impl(expand_from_impl(&struct_name)?)); + // From Impl for Fetcher and Stub + items.push(syn::Item::Impl(expand_from_impl( + &struct_name, + &syn::parse_str("::worker::Fetcher")?, + )?)); + items.push(syn::Item::Impl(expand_from_impl( + &struct_name, + &syn::parse_str("::worker::Stub")?, + )?)); } let rust_file = syn::File { diff --git a/worker-kv/Cargo.toml b/worker-kv/Cargo.toml index ac0a2ca6..786c7418 100644 --- a/worker-kv/Cargo.toml +++ b/worker-kv/Cargo.toml @@ -1,34 +1,35 @@ [package] name = "worker-kv" -version = "0.7.0" +version = "0.9.1" authors = ["Zeb Piasecki "] edition = "2018" description = "Rust bindings to Cloudflare Worker KV Stores." repository = "https://github.com/zebp/worker-kv" license = "MIT OR Apache-2.0" -[package.metadata.release] -release = false - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] js-sys.workspace = true -serde = { version = "1.0.125", features = ["derive"] } -serde_json = "1.0.64" -thiserror = "1.0.29" +serde.workspace = true +serde_json.workspace = true +thiserror = "2.0.12" wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true -serde-wasm-bindgen = "0.5.0" +serde-wasm-bindgen.workspace = true +worker.workspace = true [dev-dependencies] -fs_extra = "1.2.0" +fs_extra = "1.3.0" psutil = { git = "https://github.com/mygnu/rust-psutil", branch = "update-dependencies" } -reqwest = { version = "0.11.8", features = ["json"] } -tokio = { version = "1.5.0", features = [ +reqwest = { version = "0.12.20", features = ["json"] } +tokio = { version = "1.45.1", features = [ "rt", "macros", "rt-multi-thread", "test-util", "time", ] } + +[package.metadata] +categories = ["deprecated"] diff --git a/worker-kv/README.md b/worker-kv/README.md index 28cd1853..1bac26c1 100644 --- a/worker-kv/README.md +++ b/worker-kv/README.md @@ -32,7 +32,7 @@ kv.put("example_key", "example_value")? let (value, metadata) = kv.get("example_key").text_with_metadata::>().await?; ``` -For a more complete example check out the full [example](example). +For a more complete example check out the full [example](../examples/kv). ## How do I use futures in WebAssembly? diff --git a/worker-kv/src/lib.rs b/worker-kv/src/lib.rs index d5125786..4d29edc6 100644 --- a/worker-kv/src/lib.rs +++ b/worker-kv/src/lib.rs @@ -1,234 +1,6 @@ -//! Bindings to Cloudflare Worker's [KV](https://developers.cloudflare.com/workers/runtime-apis/kv) -//! to be used ***inside*** of a worker's context. -//! -//! # Example -//! ```ignore -//! let kv = KvStore::create("Example")?; -//! -//! // Insert a new entry into the kv. -//! kv.put("example_key", "example_value")? -//! .metadata(vec![1, 2, 3, 4]) // Use some arbitrary serialiazable metadata -//! .execute() -//! .await?; -//! -//! // NOTE: kv changes can take a minute to become visible to other workers. -//! // Get that same metadata. -//! let (value, metadata) = kv.get("example_key").text_with_metadata::>().await?; -//! ``` -#[forbid(missing_docs)] -mod builder; +#![deprecated( + since = "0.10.0", + note = "This crate has been merged into `worker`. Use `worker::kv` instead." +)] -pub use builder::*; - -use js_sys::{global, Function, Object, Promise, Reflect, Uint8Array}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use wasm_bindgen::JsValue; -use wasm_bindgen_futures::JsFuture; - -/// A binding to a Cloudflare KvStore. -#[derive(Clone)] -pub struct KvStore { - pub(crate) this: Object, - pub(crate) get_function: Function, - pub(crate) get_with_meta_function: Function, - pub(crate) put_function: Function, - pub(crate) list_function: Function, - pub(crate) delete_function: Function, -} - -// Allows for attachment to axum router, as Workers will never allow multithreading. -unsafe impl Send for KvStore {} -unsafe impl Sync for KvStore {} - -impl KvStore { - /// Creates a new [`KvStore`] with the binding specified in your `wrangler.toml`. - pub fn create(binding: &str) -> Result { - let this = get(&global(), binding)?; - - // Ensures that the kv store exists. - if this.is_undefined() { - Err(KvError::InvalidKvStore(binding.into())) - } else { - Ok(Self { - get_function: get(&this, "get")?.into(), - get_with_meta_function: get(&this, "getWithMetadata")?.into(), - put_function: get(&this, "put")?.into(), - list_function: get(&this, "list")?.into(), - delete_function: get(&this, "delete")?.into(), - this: this.into(), - }) - } - } - - /// Creates a new [`KvStore`] with the binding specified in your `wrangler.toml`, using an - /// alternative `this` value for arbitrary binding contexts. - pub fn from_this(this: &JsValue, binding: &str) -> Result { - let this = get(this, binding)?; - - // Ensures that the kv store exists. - if this.is_undefined() { - Err(KvError::InvalidKvStore(binding.into())) - } else { - Ok(Self { - get_function: get(&this, "get")?.into(), - get_with_meta_function: get(&this, "getWithMetadata")?.into(), - put_function: get(&this, "put")?.into(), - list_function: get(&this, "list")?.into(), - delete_function: get(&this, "delete")?.into(), - this: this.into(), - }) - } - } - - /// Fetches the value from the kv store by name. - pub fn get(&self, name: &str) -> GetOptionsBuilder { - GetOptionsBuilder { - this: self.this.clone(), - get_function: self.get_function.clone(), - get_with_meta_function: self.get_with_meta_function.clone(), - name: JsValue::from(name), - cache_ttl: None, - value_type: None, - } - } - - /// Puts data into the kv store. - pub fn put(&self, name: &str, value: T) -> Result { - Ok(PutOptionsBuilder { - this: self.this.clone(), - put_function: self.put_function.clone(), - name: JsValue::from(name), - value: value.raw_kv_value()?, - expiration: None, - expiration_ttl: None, - metadata: None, - }) - } - - /// Puts the specified byte slice into the kv store. - pub fn put_bytes(&self, name: &str, value: &[u8]) -> Result { - let typed_array = Uint8Array::new_with_length(value.len() as u32); - typed_array.copy_from(value); - let value: JsValue = typed_array.buffer().into(); - Ok(PutOptionsBuilder { - this: self.this.clone(), - put_function: self.put_function.clone(), - name: JsValue::from(name), - value, - expiration: None, - expiration_ttl: None, - metadata: None, - }) - } - - /// Lists the keys in the kv store. - pub fn list(&self) -> ListOptionsBuilder { - ListOptionsBuilder { - this: self.this.clone(), - list_function: self.list_function.clone(), - limit: None, - cursor: None, - prefix: None, - } - } - - /// Deletes a key in the kv store. - pub async fn delete(&self, name: &str) -> Result<(), KvError> { - let name = JsValue::from(name); - let promise: Promise = self.delete_function.call1(&self.this, &name)?.into(); - JsFuture::from(promise).await?; - Ok(()) - } -} - -/// The response for listing the elements in a KV store. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ListResponse { - /// A slice of all of the keys in the KV store. - pub keys: Vec, - /// If there are more keys that can be fetched using the response's cursor. - pub list_complete: bool, - /// A string used for paginating responses. - pub cursor: Option, -} - -/// The representation of a key in the KV store. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Key { - /// The name of the key. - pub name: String, - /// When (expressed as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time)) the key - /// value pair will expire in the store. - pub expiration: Option, - /// All metadata associated with the key. - pub metadata: Option, -} - -/// A simple error type that can occur during kv operations. -#[derive(Debug, thiserror::Error)] -pub enum KvError { - #[error("js error: {0:?}")] - JavaScript(JsValue), - #[error("unable to serialize/deserialize: {0}")] - Serialization(serde_json::Error), - #[error("invalid kv store: {0}")] - InvalidKvStore(String), -} - -impl From for JsValue { - fn from(val: KvError) -> Self { - match val { - KvError::JavaScript(value) => value, - KvError::Serialization(e) => format!("KvError::Serialization: {e}").into(), - KvError::InvalidKvStore(binding) => { - format!("KvError::InvalidKvStore: {binding}").into() - } - } - } -} - -impl From for KvError { - fn from(value: JsValue) -> Self { - Self::JavaScript(value) - } -} - -impl From for KvError { - fn from(value: serde_json::Error) -> Self { - Self::Serialization(value) - } -} - -/// A trait for things that can be converted to [`wasm_bindgen::JsValue`] to be passed to the kv. -pub trait ToRawKvValue { - fn raw_kv_value(&self) -> Result; -} - -impl ToRawKvValue for str { - fn raw_kv_value(&self) -> Result { - Ok(JsValue::from(self)) - } -} - -impl ToRawKvValue for T { - fn raw_kv_value(&self) -> Result { - let value = serde_wasm_bindgen::to_value(self).map_err(JsValue::from)?; - - if value.as_string().is_some() { - Ok(value) - } else if let Some(number) = value.as_f64() { - Ok(JsValue::from(number.to_string())) - } else if let Some(boolean) = value.as_bool() { - Ok(JsValue::from(boolean.to_string())) - } else { - js_sys::JSON::stringify(&value) - .map(JsValue::from) - .map_err(Into::into) - } - } -} - -fn get(target: &JsValue, name: &str) -> Result { - Reflect::get(target, &JsValue::from(name)) -} +pub use worker::kv::*; diff --git a/worker-macros/Cargo.toml b/worker-macros/Cargo.toml index af315114..a8142aaf 100644 --- a/worker-macros/Cargo.toml +++ b/worker-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "worker-macros" -version = "0.3.1" +version = "0.6.7" authors = ["Cloudflare Workers Team "] repository = "https://github.com/cloudflare/workers-rs/tree/main/worker-macros" edition = "2018" @@ -16,7 +16,7 @@ async-trait.workspace = true wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true wasm-bindgen-macro-support.workspace = true -worker-sys = { path = "../worker-sys", version = "0.3.1" } +worker-sys.workspace = true syn = "2.0.17" proc-macro2 = "1.0.60" quote = "1.0.28" diff --git a/worker-macros/src/durable_object.rs b/worker-macros/src/durable_object.rs index 865453b3..ddcaed60 100644 --- a/worker-macros/src/durable_object.rs +++ b/worker-macros/src/durable_object.rs @@ -1,289 +1,205 @@ use proc_macro2::{Ident, TokenStream}; -use quote::{quote, ToTokens}; -use syn::{spanned::Spanned, Error, FnArg, ImplItem, Item, Type, TypePath, Visibility}; - -pub fn expand_macro(tokens: TokenStream) -> syn::Result { - let item = syn::parse2::(tokens)?; - match item { - Item::Impl(imp) => { - let impl_token = imp.impl_token; - let trai = imp.trait_.clone(); - let (_, trai, _) = trai.ok_or_else(|| Error::new_spanned(impl_token, "Must be a DurableObject trait impl"))?; - if !trai.segments.last().map(|x| x.ident == "DurableObject").unwrap_or(false) { - return Err(Error::new(trai.span(), "Must be a DurableObject trait impl")) - } - - let pound = syn::Token![#](imp.span()).to_token_stream(); - let wasm_bindgen_attr = quote! {#pound[wasm_bindgen::prelude::wasm_bindgen]}; +use quote::quote; +use syn::{Error, ItemImpl, ItemStruct}; +enum DurableObjectType { + Fetch, + Alarm, + WebSocket, +} +impl syn::parse::Parse for DurableObjectType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let ident = input.parse::()?; + match &*ident.to_string() { + "fetch" => Ok(Self::Fetch), + "alarm" => Ok(Self::Alarm), + "websocket" => Ok(Self::WebSocket), + _ => Err(Error::new(ident.span(), "must have either 'fetch', 'alarm' or 'websocket' attribute, e.g. #[durable_object(websocket)]")) + } + } +} - let struct_name = imp.self_ty; - let items = imp.items; - let mut tokenized = vec![]; +mod bindgen_methods { + use proc_macro2::TokenStream; + use quote::quote; + + pub fn core() -> TokenStream { + quote! { + #[wasm_bindgen(constructor, wasm_bindgen=wasm_bindgen)] + pub fn new( + state: ::worker::worker_sys::DurableObjectState, + env: ::worker::Env + ) -> Self { + ::new( + ::worker::durable::State::from(state), + env + ) + } - #[derive(Default)] - struct OptionalMethods { - has_alarm: bool, - has_websocket_message: bool, - has_websocket_close: bool, - has_websocket_error: bool, + #[wasm_bindgen(js_name = fetch, wasm_bindgen=wasm_bindgen)] + pub fn fetch( + &self, + req: ::worker::worker_sys::web_sys::Request + ) -> ::worker::js_sys::Promise { + // SAFETY: + // Durable Object will never be destroyed while there is still + // a running promise inside of it, therefore we can let a reference + // to the durable object escape into a static-lifetime future. + let static_self: &'static Self = unsafe { &*(self as *const _) }; + + ::worker::wasm_bindgen_futures::future_to_promise(async move { + ::fetch(static_self, req.into()).await + .map(::worker::worker_sys::web_sys::Response::from) + .map(::worker::wasm_bindgen::JsValue::from) + .map_err(::worker::wasm_bindgen::JsValue::from) + }) } + } + } - let mut optional_methods = OptionalMethods::default(); + pub fn alarm() -> TokenStream { + quote! { + #[wasm_bindgen(js_name = alarm, wasm_bindgen=wasm_bindgen)] + pub fn alarm(&self) -> ::worker::js_sys::Promise { + // SAFETY: + // Durable Object will never be destroyed while there is still + // a running promise inside of it, therefore we can let a reference + // to the durable object escape into a static-lifetime future. + let static_self: &'static Self = unsafe { &*(self as *const _) }; + + ::worker::wasm_bindgen_futures::future_to_promise(async move { + ::alarm(static_self).await + .map(::worker::worker_sys::web_sys::Response::from) + .map(::worker::wasm_bindgen::JsValue::from) + .map_err(::worker::wasm_bindgen::JsValue::from) + }) + } + } + } - for item in items { - let impl_method = match item { - ImplItem::Fn(func) => func, - _ => return Err(Error::new_spanned(item, "Impl block must only contain methods")) + pub fn websocket() -> TokenStream { + quote! { + #[wasm_bindgen(js_name = webSocketMessage, wasm_bindgen=wasm_bindgen)] + pub fn websocket_message( + &self, + ws: ::worker::worker_sys::web_sys::WebSocket, + message: ::worker::wasm_bindgen::JsValue + ) -> ::worker::js_sys::Promise { + let message = match message.as_string() { + Some(message) => ::worker::WebSocketIncomingMessage::String(message), + None => ::worker::WebSocketIncomingMessage::Binary( + ::worker::js_sys::Uint8Array::new(&message).to_vec() + ) }; - let span = impl_method.sig.ident.span(); - - let tokens = match impl_method.sig.ident.to_string().as_str() { - "new" => { - let mut method = impl_method.clone(); - method.sig.ident = Ident::new("_new", method.sig.ident.span()); - method.vis = Visibility::Inherited; - - - // modify the `state` argument so it is type ObjectState - let arg_tokens = method.sig.inputs.first_mut().expect("DurableObject `new` method must have 2 arguments: state and env").into_token_stream(); - match syn::parse2::(arg_tokens)? { - FnArg::Typed(pat) => { - let path = syn::parse2::(quote!{worker::worker_sys::DurableObjectState})?; - let mut updated_pat = pat; - updated_pat.ty = Box::new(Type::Path(path)); - - let state_arg = FnArg::Typed(updated_pat); - let env_arg = method.sig.inputs.pop().expect("DurableObject `new` method expects a second argument: env"); - method.sig.inputs.clear(); - method.sig.inputs.insert(0, state_arg); - method.sig.inputs.insert(1, env_arg.into_value()) - }, - _ => return Err(Error::new(method.sig.inputs.span(), "DurableObject `new` method expects `state: State` as first argument.")) - } - - // prepend the function block's statements to convert the ObjectState to State type - let mut prepended = vec![syn::parse_quote! { - let state = ::worker::durable::State::from(state); - }]; - prepended.extend(method.block.stmts); - method.block.stmts = prepended; - - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen(constructor)] - pub #method - }) - }, - "fetch" => { - let mut method = impl_method.clone(); - method.sig.ident = Ident::new("_fetch_raw", method.sig.ident.span()); - method.vis = Visibility::Inherited; - - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen(js_name = fetch)] - pub fn _fetch(&mut self, req: worker::worker_sys::web_sys::Request) -> worker::js_sys::Promise { - // SAFETY: - // On the surface, this is unsound because the Durable Object could be dropped - // while JavaScript still has possession of the future. However, - // we know something that Rust doesn't: that the Durable Object will never be destroyed - // while there is still a running promise inside of it, therefore we can let a reference - // to the durable object escape into a static-lifetime future. - let static_self: &'static mut Self = unsafe {&mut *(self as *mut _)}; - - wasm_bindgen_futures::future_to_promise(async move { - static_self._fetch_raw(req.into()).await.map(worker::worker_sys::web_sys::Response::from).map(wasm_bindgen::JsValue::from) - .map_err(wasm_bindgen::JsValue::from) - }) - } - - #method - }) - }, - "alarm" => { - optional_methods.has_alarm = true; - - let mut method = impl_method.clone(); - method.sig.ident = Ident::new("_alarm_raw", method.sig.ident.span()); - method.vis = Visibility::Inherited; - - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen(js_name = alarm)] - pub fn _alarm(&mut self) -> worker::js_sys::Promise { - // SAFETY: - // On the surface, this is unsound because the Durable Object could be dropped - // while JavaScript still has possession of the future. However, - // we know something that Rust doesn't: that the Durable Object will never be destroyed - // while there is still a running promise inside of it, therefore we can let a reference - // to the durable object escape into a static-lifetime future. - let static_self: &'static mut Self = unsafe {&mut *(self as *mut _)}; - - wasm_bindgen_futures::future_to_promise(async move { - static_self._alarm_raw().await.map(worker::worker_sys::web_sys::Response::from).map(wasm_bindgen::JsValue::from) - .map_err(wasm_bindgen::JsValue::from) - }) - } - - #method - }) - }, - "websocket_message" => { - optional_methods.has_websocket_message = true; - - let mut method = impl_method.clone(); - method.sig.ident = Ident::new("_websocket_message_raw", method.sig.ident.span()); - method.vis = Visibility::Inherited; - - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen(js_name = webSocketMessage)] - pub fn _websocket_message(&mut self, ws: worker::worker_sys::web_sys::WebSocket, message: wasm_bindgen::JsValue) -> worker::js_sys::Promise { - let ws_message = if let Some(string_message) = message.as_string() { - worker::WebSocketIncomingMessage::String(string_message) - } else { - let v = worker::js_sys::Uint8Array::new(&message).to_vec(); - worker::WebSocketIncomingMessage::Binary(v) - }; - - // SAFETY: - // On the surface, this is unsound because the Durable Object could be dropped - // while JavaScript still has possession of the future. However, - // we know something that Rust doesn't: that the Durable Object will never be destroyed - // while there is still a running promise inside of it, therefore we can let a reference - // to the durable object escape into a static-lifetime future. - let static_self: &'static mut Self = unsafe {&mut *(self as *mut _)}; - - wasm_bindgen_futures::future_to_promise(async move { - static_self._websocket_message_raw(ws.into(), ws_message).await.map(|_| wasm_bindgen::JsValue::NULL) - .map_err(wasm_bindgen::JsValue::from) - }) - } - - #method - }) - }, - "websocket_close" => { - optional_methods.has_websocket_close = true; - - let mut method = impl_method.clone(); - method.sig.ident = Ident::new("_websocket_close_raw", method.sig.ident.span()); - method.vis = Visibility::Inherited; - - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen(js_name = webSocketClose)] - pub fn _websocket_close(&mut self, ws: worker::worker_sys::web_sys::WebSocket, code: usize, reason: String, was_clean: bool) -> worker::js_sys::Promise { - // SAFETY: - // On the surface, this is unsound because the Durable Object could be dropped - // while JavaScript still has possession of the future. However, - // we know something that Rust doesn't: that the Durable Object will never be destroyed - // while there is still a running promise inside of it, therefore we can let a reference - // to the durable object escape into a static-lifetime future. - let static_self: &'static mut Self = unsafe {&mut *(self as *mut _)}; - - wasm_bindgen_futures::future_to_promise(async move { - static_self._websocket_close_raw(ws.into(), code, reason, was_clean).await.map(|_| wasm_bindgen::JsValue::NULL) - .map_err(wasm_bindgen::JsValue::from) - }) - } - - #method - }) - }, - "websocket_error" => { - optional_methods.has_websocket_error = true; - - let mut method = impl_method.clone(); - method.sig.ident = Ident::new("_websocket_error_raw", method.sig.ident.span()); - method.vis = Visibility::Inherited; - - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen(js_name = webSocketError)] - pub fn _websocket_error(&mut self, ws: worker::worker_sys::web_sys::WebSocket, error: wasm_bindgen::JsValue) -> worker::js_sys::Promise { - // SAFETY: - // On the surface, this is unsound because the Durable Object could be dropped - // while JavaScript still has possession of the future. However, - // we know something that Rust doesn't: that the Durable Object will never be destroyed - // while there is still a running promise inside of it, therefore we can let a reference - // to the durable object escape into a static-lifetime future. - let static_self: &'static mut Self = unsafe {&mut *(self as *mut _)}; - - wasm_bindgen_futures::future_to_promise(async move { - static_self._websocket_error_raw(ws.into(), error.into()).await.map(|_| wasm_bindgen::JsValue::NULL) - .map_err(wasm_bindgen::JsValue::from) - }) - } - - #method - }) - }, - ident => Err(Error::new(span, format!("Unsupported method `{}`, please move extra impl methods to a separate impl definition", ident))) - }; - tokenized.push(tokens?); + // SAFETY: + // Durable Object will never be destroyed while there is still + // a running promise inside of it, therefore we can let a reference + // to the durable object escape into a static-lifetime future. + let static_self: &'static Self = unsafe { &*(self as *const _) }; + + ::worker::wasm_bindgen_futures::future_to_promise(async move { + ::websocket_message(static_self, ws.into(), message).await + .map(|_| ::worker::wasm_bindgen::JsValue::NULL) + .map_err(::worker::wasm_bindgen::JsValue::from) + }) } - let alarm_tokens = optional_methods.has_alarm.then(|| quote! { - async fn alarm(&mut self) -> ::worker::Result { - self._alarm_raw().await - } - }); + #[wasm_bindgen(js_name = webSocketClose, wasm_bindgen=wasm_bindgen)] + pub fn websocket_close( + &self, + ws: ::worker::worker_sys::web_sys::WebSocket, + code: usize, + reason: String, + was_clean: bool + ) -> ::worker::js_sys::Promise { + // SAFETY: + // Durable Object will never be destroyed while there is still + // a running promise inside of it, therefore we can let a reference + // to the durable object escape into a static-lifetime future. + let static_self: &'static Self = unsafe { &*(self as *const _) }; + + ::worker::wasm_bindgen_futures::future_to_promise(async move { + ::websocket_close(static_self, ws.into(), code, reason, was_clean).await + .map(|_| ::worker::wasm_bindgen::JsValue::NULL) + .map_err(::worker::wasm_bindgen::JsValue::from) + }) + } - let websocket_message_tokens = optional_methods.has_websocket_message.then(|| quote! { - async fn websocket_message(&mut self, ws: ::worker::WebSocket, message: ::worker::WebSocketIncomingMessage) -> ::worker::Result<()> { - self._websocket_message_raw(ws, message).await - } - }); + #[wasm_bindgen(js_name = webSocketError, wasm_bindgen=wasm_bindgen)] + pub fn websocket_error( + &self, + ws: ::worker::worker_sys::web_sys::WebSocket, + error: ::worker::wasm_bindgen::JsValue + ) -> ::worker::js_sys::Promise { + // SAFETY: + // Durable Object will never be destroyed while there is still + // a running promise inside of it, therefore we can let a reference + // to the durable object escape into a static-lifetime future. + let static_self: &'static Self = unsafe { &*(self as *const _) }; + + ::worker::wasm_bindgen_futures::future_to_promise(async move { + ::websocket_error(static_self, ws.into(), error.into()).await + .map(|_| ::worker::wasm_bindgen::JsValue::NULL) + .map_err(::worker::wasm_bindgen::JsValue::from) + }) + } + } + } +} - let websocket_close_tokens = optional_methods.has_websocket_close.then(|| quote! { - async fn websocket_close(&mut self, ws: ::worker::WebSocket, code: usize, reason: String, was_clean: bool) -> ::worker::Result<()> { - self._websocket_close_raw(ws, code, reason, was_clean).await - } - }); +pub fn expand_macro(attr: TokenStream, tokens: TokenStream) -> syn::Result { + // Try to give a nice error for previous impl usage + if syn::parse2::(tokens.clone()).is_ok() { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + "The #[durable_object] macro is no longer required for `impl` blocks, and can be removed" + )); + } - let websocket_error_tokens = optional_methods.has_websocket_error.then(|| quote! { - async fn websocket_error(&mut self, ws: ::worker::WebSocket, error: ::worker::Error) -> ::worker::Result<()> { - self._websocket_error_raw(ws, error).await - } - }); + let target = syn::parse2::(tokens)?; - Ok(quote! { - #wasm_bindgen_attr - impl #struct_name { - #(#tokenized)* - } + let durable_object_type = (!attr.is_empty()) + .then(|| syn::parse2::(attr)) + .transpose()?; - #pound[async_trait::async_trait(?Send)] - impl ::worker::durable::DurableObject for #struct_name { - fn new(state: ::worker::durable::State, env: ::worker::Env) -> Self { - Self::_new(state._inner(), env) - } + let bindgen_methods = match durable_object_type { + // if not specified, bindgen all. + // this is expected behavior, and is also required for #[durable_object] to compile and work + None => vec![ + bindgen_methods::core(), + bindgen_methods::alarm(), + bindgen_methods::websocket(), + ], - async fn fetch(&mut self, req: ::worker::Request) -> ::worker::Result { - self._fetch_raw(req).await - } + // if specified, bindgen only related methods. + Some(DurableObjectType::Fetch) => vec![bindgen_methods::core()], + Some(DurableObjectType::Alarm) => vec![bindgen_methods::core(), bindgen_methods::alarm()], + Some(DurableObjectType::WebSocket) => { + vec![bindgen_methods::core(), bindgen_methods::websocket()] + } + }; - #alarm_tokens + let target_name = &target.ident; + Ok(quote! { + #target - #websocket_message_tokens + #[allow(unused_imports)] + use ::worker::DurableObject; - #websocket_close_tokens + impl ::worker::has_durable_object_attribute for #target_name {} - #websocket_error_tokens - } + const _: () = { + use ::worker::wasm_bindgen::prelude::*; - trait __Need_Durable_Object_Trait_Impl_With_durable_object_Attribute { const MACROED: bool = true; } - impl __Need_Durable_Object_Trait_Impl_With_durable_object_Attribute for #struct_name {} - }) - }, - Item::Struct(struc) => { - let tokens = struc.to_token_stream(); - let pound = syn::Token![#](struc.span()).to_token_stream(); - let struct_name = struc.ident; - Ok(quote! { - #pound[wasm_bindgen::prelude::wasm_bindgen] - #tokens + #[wasm_bindgen(wasm_bindgen=wasm_bindgen)] + #[::worker::consume] + #target - const _: bool = <#struct_name as __Need_Durable_Object_Trait_Impl_With_durable_object_Attribute>::MACROED; - }) - }, - _ => Err(Error::new(item.span(), "Durable Object macro can only be applied to structs and their impl of DurableObject trait")) - } + #[wasm_bindgen(wasm_bindgen=wasm_bindgen)] + impl #target_name { + #(#bindgen_methods)* + } + }; + }) } diff --git a/worker-macros/src/lib.rs b/worker-macros/src/lib.rs index 892c5309..c6f2cd37 100644 --- a/worker-macros/src/lib.rs +++ b/worker-macros/src/lib.rs @@ -4,9 +4,58 @@ mod send; use proc_macro::TokenStream; +/// Integrate the struct with the Workers Runtime as Durable Object.\ +/// Requires the `DurableObject` trait with the durable_object attribute macro on the struct. +/// +/// ## Example +/// +/// ```rust +/// #[durable_object] +/// pub struct Chatroom { +/// users: Vec, +/// messages: Vec, +/// state: State, +/// env: Env, // access `Env` across requests, use inside `fetch` +/// } +/// +/// impl DurableObject for Chatroom { +/// fn new(state: State, env: Env) -> Self { +/// Self { +/// users: vec![], +/// messages: vec![], +/// state, +/// env, +/// } +/// } +/// +/// async fn fetch(&self, _req: Request) -> Result { +/// // do some work when a worker makes a request to this DO +/// Response::ok(&format!("{} active users.", self.users.len())) +/// } +/// } +/// ``` +/// +/// ## Note +/// +/// By default all durable object events are enabled. +/// Arguments may be provided to the macro to only generate the desired events, and reduce the generated JS & Wasm output: +/// +/// * `fetch`: simple `fetch` target +/// * `alarm`: with [Alarms API](https://developers.cloudflare.com/durable-objects/examples/alarms-api/) +/// * `websocket`: [WebSocket server](https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server/) +/// +/// ```rust +/// #[durable_object(fetch)] +/// pub struct Chatroom { +/// users: Vec, +/// messages: Vec, +/// state: State, +/// env: Env, // access `Env` across requests, use inside `fetch` +/// } +/// ``` #[proc_macro_attribute] -pub fn durable_object(_attr: TokenStream, item: TokenStream) -> TokenStream { - durable_object::expand_macro(item.into()) +pub fn durable_object(attr: TokenStream, item: TokenStream) -> TokenStream { + durable_object::expand_macro(attr.into(), item.into()) .unwrap_or_else(syn::Error::into_compile_error) .into() } @@ -86,3 +135,9 @@ pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream { pub fn send(attr: TokenStream, stream: TokenStream) -> TokenStream { send::expand_macro(attr, stream) } + +#[doc(hidden)] +#[proc_macro_attribute] +pub fn consume(_: TokenStream, _: TokenStream) -> TokenStream { + TokenStream::new() +} diff --git a/worker-sandbox/Cargo.toml b/worker-sandbox/Cargo.toml deleted file mode 100644 index 1310ce7b..00000000 --- a/worker-sandbox/Cargo.toml +++ /dev/null @@ -1,66 +0,0 @@ -[package] -authors = ["Cloudflare Workers Team "] -edition = "2018" -name = "worker-sandbox" -version = "0.1.0" -license = "Apache-2.0" - -[package.metadata.release] -release = false - -[lib] -crate-type = ["cdylib", "rlib"] -path = "src/lib.rs" - -[features] -default = ["console_error_panic_hook"] -http = ["worker/http", "worker/axum", "dep:axum", "dep:tower-service", "dep:axum-macros"] - -[dependencies] -futures-channel.workspace = true -futures-util.workspace = true -blake2 = "0.10.6" -chrono = { version = "0.4.35", default-features = false, features = [ - "wasmbind", - "clock", -] } -cfg-if = "1.0.0" -console_error_panic_hook = { version = "0.1.7", optional = true } -getrandom = { version = "0.2.10", features = ["js"] } -hex = "0.4.3" -http.workspace=true -regex = "1.8.4" -serde = { version = "1.0.164", features = ["derive"] } -serde_json = "1.0.96" -worker = { path = "../worker", version = "0.3.1", features = ["queue", "d1"] } -rand = "0.8.5" -uuid = { version = "1.3.3", features = ["v4", "serde"] } -serde-wasm-bindgen = "0.6.1" -md5 = "0.7.0" -tokio-stream = "0.1.15" -tokio = { version = "1.28", default-features = false, features=['io-util'] } -worker-kv = { path = "../worker-kv" } - -[dependencies.axum] -version = "0.7" -optional = true -default-features = false - -[dependencies.axum-macros] -version = "0.4" -optional = true -default-features = false - -[dependencies.tower-service] -version = "0.3" -optional = true - -[dev-dependencies] -wasm-bindgen-test.workspace = true -futures-channel = { version = "0.3.29", features = ["sink"] } -futures-util = { version = "0.3.29", default-features = false, features = [ - "sink", -] } -tokio = { version = "1.28.2", features = ["macros", "rt", "test-util"] } -tungstenite = "0.21" -retry = "2.0.0" diff --git a/worker-sandbox/src/alarm.rs b/worker-sandbox/src/alarm.rs deleted file mode 100644 index 49864b19..00000000 --- a/worker-sandbox/src/alarm.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::time::Duration; -use tokio_stream::{StreamExt, StreamMap}; - -use worker::*; - -use super::SomeSharedData; - -#[durable_object] -pub struct AlarmObject { - state: State, -} - -#[durable_object] -impl DurableObject for AlarmObject { - fn new(state: State, _: Env) -> Self { - Self { state } - } - - async fn fetch(&mut self, _: Request) -> Result { - let alarmed: bool = match self.state.storage().get("alarmed").await { - Ok(alarmed) => alarmed, - Err(e) if e.to_string() == "No such value in storage." => { - // Trigger our alarm method in 100ms. - self.state - .storage() - .set_alarm(Duration::from_millis(100)) - .await?; - - false - } - Err(e) => return Err(e), - }; - - Response::ok(alarmed.to_string()) - } - - async fn alarm(&mut self) -> Result { - self.state.storage().put("alarmed", true).await?; - - console_log!("Alarm has been triggered!"); - - Response::ok("ALARMED") - } -} - -#[worker::send] -pub async fn handle_alarm(_req: Request, env: Env, _data: SomeSharedData) -> Result { - let namespace = env.durable_object("ALARM")?; - let stub = namespace.id_from_name("alarm")?.get_stub()?; - // when calling fetch to a Durable Object, a full URL must be used. Alternatively, a - // compatibility flag can be provided in wrangler.toml to opt-in to older behavior: - // https://developers.cloudflare.com/workers/platform/compatibility-dates#durable-object-stubfetch-requires-a-full-url - stub.fetch_with_str("https://fake-host/alarm").await -} - -#[worker::send] -pub async fn handle_id(_req: Request, env: Env, _data: SomeSharedData) -> Result { - let namespace = env.durable_object("COUNTER").expect("DAWJKHDAD"); - let stub = namespace.id_from_name("A")?.get_stub()?; - // when calling fetch to a Durable Object, a full URL must be used. Alternatively, a - // compatibility flag can be provided in wrangler.toml to opt-in to older behavior: - // https://developers.cloudflare.com/workers/platform/compatibility-dates#durable-object-stubfetch-requires-a-full-url - stub.fetch_with_str("https://fake-host/").await -} - -#[worker::send] -pub async fn handle_put_raw(req: Request, env: Env, _data: SomeSharedData) -> Result { - let namespace = env.durable_object("PUT_RAW_TEST_OBJECT")?; - let id = namespace.unique_id()?; - let stub = id.get_stub()?; - stub.fetch_with_request(req).await -} - -#[worker::send] -pub async fn handle_websocket(_req: Request, env: Env, _data: SomeSharedData) -> Result { - // Accept / handle a websocket connection - let pair = WebSocketPair::new()?; - let server = pair.server; - server.accept()?; - - // Connect to Durable Object via WS - let namespace = env - .durable_object("COUNTER") - .expect("failed to get namespace"); - let stub = namespace.id_from_name("A")?.get_stub()?; - let mut req = Request::new("https://fake-host/ws", Method::Get)?; - req.headers_mut()?.set("upgrade", "websocket")?; - - let res = stub.fetch_with_request(req).await?; - let do_ws = res.websocket().expect("server did not accept websocket"); - do_ws.accept()?; - - wasm_bindgen_futures::spawn_local(async move { - let event_stream = server.events().expect("could not open stream"); - let do_event_stream = do_ws.events().expect("could not open stream"); - - let mut map = StreamMap::new(); - map.insert("client", event_stream); - map.insert("durable", do_event_stream); - - while let Some((key, event)) = map.next().await { - match key { - "client" => match event.expect("received error in websocket") { - WebsocketEvent::Message(msg) => { - if let Some(text) = msg.text() { - do_ws.send_with_str(text).expect("could not relay text"); - } - } - WebsocketEvent::Close(_) => { - let _res = do_ws.close(Some(1000), Some("client closed".to_string())); - } - }, - "durable" => match event.expect("received error in websocket") { - WebsocketEvent::Message(msg) => { - if let Some(text) = msg.text() { - server.send_with_str(text).expect("could not relay text"); - } - } - WebsocketEvent::Close(_) => { - let _res = server.close(Some(1000), Some("durable closed".to_string())); - } - }, - _ => unreachable!(), - } - } - }); - - Response::from_websocket(pair.client) -} diff --git a/worker-sandbox/src/counter.rs b/worker-sandbox/src/counter.rs deleted file mode 100644 index d421efba..00000000 --- a/worker-sandbox/src/counter.rs +++ /dev/null @@ -1,84 +0,0 @@ -use worker::*; - -#[durable_object] -pub struct Counter { - count: usize, - state: State, - initialized: bool, - env: Env, -} - -#[durable_object] -impl DurableObject for Counter { - fn new(state: State, env: Env) -> Self { - Self { - count: 0, - initialized: false, - state, - env, - } - } - - async fn fetch(&mut self, req: Request) -> Result { - if !self.initialized { - self.initialized = true; - self.count = self.state.storage().get("count").await.unwrap_or(0); - } - - if req.path().eq("/ws") { - let pair = WebSocketPair::new()?; - let server = pair.server; - // accept websocket with hibernation api - self.state.accept_web_socket(&server); - server - .serialize_attachment("hello") - .expect("failed to serialize attachment"); - - return Ok(ResponseBuilder::new() - .with_status(101) - .with_websocket(pair.client) - .empty()); - } - - self.count += 10; - self.state.storage().put("count", self.count).await?; - - Response::ok(format!( - "[durable_object]: self.count: {}, secret value: {}", - self.count, - self.env.secret("SOME_SECRET")? - )) - } - - async fn websocket_message( - &mut self, - ws: WebSocket, - _message: WebSocketIncomingMessage, - ) -> Result<()> { - let _attach: String = ws - .deserialize_attachment()? - .expect("websockets should have an attachment"); - // get and increment storage by 10 - let mut count: usize = self.state.storage().get("count").await.unwrap_or(0); - count += 10; - self.state.storage().put("count", count).await?; - // send value to client - ws.send_with_str(format!("{}", count)) - .expect("failed to send value to client"); - Ok(()) - } - - async fn websocket_close( - &mut self, - _ws: WebSocket, - _code: usize, - _reason: String, - _was_clean: bool, - ) -> Result<()> { - Ok(()) - } - - async fn websocket_error(&mut self, _ws: WebSocket, _error: Error) -> Result<()> { - Ok(()) - } -} diff --git a/worker-sandbox/src/d1.rs b/worker-sandbox/src/d1.rs deleted file mode 100644 index 535f25ac..00000000 --- a/worker-sandbox/src/d1.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::SomeSharedData; -use serde::Deserialize; -use worker::*; - -#[derive(Deserialize)] -struct Person { - id: u32, - name: String, - age: u32, -} - -#[worker::send] -pub async fn prepared_statement( - _req: Request, - env: Env, - _data: SomeSharedData, -) -> Result { - let db = env.d1("DB")?; - let unbound_stmt = worker::query!(&db, "SELECT * FROM people WHERE name = ?"); - - let stmt = unbound_stmt.bind_refs(&D1Type::Text("Ryan Upton"))?; - - // All rows - let results = stmt.all().await?; - let people = results.results::()?; - - assert!(results.success()); - assert_eq!(results.error(), None); - assert_eq!(people.len(), 1); - assert_eq!(people[0].name, "Ryan Upton"); - assert_eq!(people[0].age, 21); - assert_eq!(people[0].id, 6); - - // All columns of the first rows - let person = stmt.first::(None).await?.unwrap(); - assert_eq!(person.name, "Ryan Upton"); - assert_eq!(person.age, 21); - - // The name of the first row - let name = stmt.first::(Some("name")).await?.unwrap(); - assert_eq!(name, "Ryan Upton"); - - // All of the rows as column arrays of raw JSON values. - let rows = stmt.raw::().await?; - assert_eq!(rows.len(), 1); - let columns = &rows[0]; - - assert_eq!(columns[0].as_u64(), Some(6)); - assert_eq!(columns[1].as_str(), Some("Ryan Upton")); - assert_eq!(columns[2].as_u64(), Some(21)); - - let stmt_2 = unbound_stmt.bind_refs([&D1Type::Text("John Smith")])?; - let person = stmt_2.first::(None).await?.unwrap(); - assert_eq!(person.name, "John Smith"); - assert_eq!(person.age, 92); - - let prepared_argument = D1PreparedArgument::new(&D1Type::Text("Dorian Fischer")); - let stmt_3 = unbound_stmt.bind_refs(&prepared_argument)?; - let person = stmt_3.first::(None).await?.unwrap(); - assert_eq!(person.name, "Dorian Fischer"); - assert_eq!(person.age, 19); - - Response::ok("ok") -} - -#[worker::send] -pub async fn batch(_req: Request, env: Env, _data: SomeSharedData) -> Result { - let db = env.d1("DB")?; - let mut results = db - .batch(vec![ - worker::query!(&db, "SELECT * FROM people WHERE id < 4"), - worker::query!(&db, "SELECT * FROM people WHERE id > 4"), - ]) - .await? - .into_iter(); - - let first_results = results.next().unwrap().results::()?; - assert_eq!(first_results.len(), 3); - assert_eq!(first_results[0].id, 1); - assert_eq!(first_results[1].id, 2); - assert_eq!(first_results[2].id, 3); - - let second_results = results.next().unwrap().results::()?; - assert_eq!(second_results.len(), 2); - assert_eq!(second_results[0].id, 5); - assert_eq!(second_results[1].id, 6); - - Response::ok("ok") -} - -#[worker::send] -pub async fn exec(mut req: Request, env: Env, _data: SomeSharedData) -> Result { - let db = env.d1("DB")?; - let result = db - .exec(req.text().await?.as_ref()) - .await - .expect("doesn't exist"); - - Response::ok(result.count()?.unwrap_or_default().to_string()) -} - -#[worker::send] -pub async fn dump(_req: Request, env: Env, _data: SomeSharedData) -> Result { - let db = env.d1("DB")?; - let bytes = db.dump().await?; - Response::from_bytes(bytes) -} - -#[worker::send] -pub async fn error(_req: Request, env: Env, _data: SomeSharedData) -> Result { - let db = env.d1("DB")?; - let error = db - .exec("THIS IS NOT VALID SQL") - .await - .expect_err("did not get error"); - - if let Error::D1(error) = error { - assert_eq!( - error.cause(), - "Error in line 1: THIS IS NOT VALID SQL: near \"THIS\": syntax error at offset 0" - ) - } else { - panic!("expected D1 error"); - } - - Response::ok("") -} diff --git a/worker-sandbox/src/router.rs b/worker-sandbox/src/router.rs deleted file mode 100644 index bcbd7a89..00000000 --- a/worker-sandbox/src/router.rs +++ /dev/null @@ -1,410 +0,0 @@ -use crate::{ - alarm, cache, d1, fetch, form, kv, queue, r2, request, service, socket, user, ws, - SomeSharedData, GLOBAL_STATE, -}; -#[cfg(feature = "http")] -use std::convert::TryInto; -use std::sync::atomic::Ordering; - -use worker::{console_log, Env, Fetch, Request, Response, ResponseBuilder, Result}; - -#[cfg(not(feature = "http"))] -use worker::{RouteContext, Router}; - -#[cfg(feature = "http")] -use axum::{ - routing::{delete, get, head, options, patch, post, put}, - Extension, -}; - -/// Rewrites a handler with legacy http types to use axum extractors / response type. -#[cfg(feature = "http")] -macro_rules! handler ( - ($name:path) => { - |Extension(env): Extension, Extension(data): Extension, req: axum::extract::Request| async { - let resp = $name(req.try_into().expect("convert request"), env, data).await.expect("handler result"); - Into::>::into(resp) - } - } -); - -#[cfg(feature = "http")] -macro_rules! handler_sync ( - ($name:path) => { - |Extension(env): Extension, Extension(data): Extension, req: axum::extract::Request| async { - let resp = $name(req.try_into().expect("convert request"), env, data).expect("handler result"); - Into::>::into(resp) - } - } -); - -#[cfg(not(feature = "http"))] -macro_rules! handler ( - ($name:path) => { - |req: Request, ctx: RouteContext| async { - $name(req, ctx.env, ctx.data).await - } - } -); - -#[cfg(not(feature = "http"))] -macro_rules! handler_sync ( - ($name:path) => { - |req: Request, ctx: RouteContext| { - $name(req, ctx.env, ctx.data) - } - } -); - -#[cfg(feature = "http")] -pub fn make_router(data: SomeSharedData, env: Env) -> axum::Router { - axum::Router::new() - .route("/request", get(handler_sync!(request::handle_a_request))) - .route( - "/async-request", - get(handler!(request::handle_async_request)), - ) - .route("/websocket", get(handler!(ws::handle_websocket))) - .route("/got-close-event", get(handler!(handle_close_event))) - .route("/ws-client", get(handler!(ws::handle_websocket_client))) - .route("/test-data", get(handler!(request::handle_test_data))) - .route("/xor/:num", post(handler!(request::handle_xor))) - .route("/headers", post(handler!(request::handle_headers))) - .route("/formdata-name", post(handler!(form::handle_formdata_name))) - .route("/is-secret", post(handler!(form::handle_is_secret))) - .route( - "/formdata-file-size", - post(handler!(form::handle_formdata_file_size)), - ) - .route( - "/formdata-file-size/:hash", - get(handler!(form::handle_formdata_file_size_hash)), - ) - .route( - "/post-file-size", - post(handler!(request::handle_post_file_size)), - ) - .route("/user/:id/test", get(handler!(user::handle_user_id_test))) - .route("/user/:id", get(handler!(user::handle_user_id))) - .route( - "/account/:id/zones", - post(handler!(user::handle_post_account_id_zones)), - ) - .route( - "/account/:id/zones", - get(handler!(user::handle_get_account_id_zones)), - ) - .route( - "/async-text-echo", - post(handler!(request::handle_async_text_echo)), - ) - .route("/fetch", get(handler!(fetch::handle_fetch))) - .route("/fetch_json", get(handler!(fetch::handle_fetch_json))) - .route( - "/proxy_request/*url", - get(handler!(fetch::handle_proxy_request)), - ) - .route("/durable/alarm", get(handler!(alarm::handle_alarm))) - .route("/durable/:id", get(handler!(alarm::handle_id))) - .route("/durable/put-raw", get(handler!(alarm::handle_put_raw))) - .route("/durable/websocket", get(handler!(alarm::handle_websocket))) - .route("/var", get(handler!(request::handle_var))) - .route("/secret", get(handler!(request::handle_secret))) - .route("/kv/:key/:value", post(handler!(kv::handle_post_key_value))) - .route("/bytes", get(handler!(request::handle_bytes))) - .route("/api-data", post(handler!(request::handle_api_data))) - .route( - "/nonsense-repeat", - post(handler!(request::handle_nonsense_repeat)), - ) - .route("/status/:code", get(handler!(request::handle_status))) - .route("/", put(handler_sync!(respond))) - .route("/", patch(handler_sync!(respond))) - .route("/", delete(handler_sync!(respond))) - .route("/", head(handler_sync!(respond))) - .route("/async", put(handler!(respond_async))) - .route("/async", patch(handler!(respond_async))) - .route("/async", delete(handler!(respond_async))) - .route("/async", head(handler!(respond_async))) - .route("/*catchall", options(handler!(handle_options_catchall))) - .route( - "/request-init-fetch", - get(handler!(fetch::handle_request_init_fetch)), - ) - .route( - "/request-init-fetch-post", - get(handler!(fetch::handle_request_init_fetch_post)), - ) - .route( - "/cancelled-fetch", - get(handler!(fetch::handle_cancelled_fetch)), - ) - .route("/fetch-timeout", get(handler!(fetch::handle_fetch_timeout))) - .route( - "/redirect-default", - get(handler!(request::handle_redirect_default)), - ) - .route("/redirect-307", get(handler!(request::handle_redirect_307))) - .route("/now", get(handler!(request::handle_now))) - .route("/cloned", get(handler!(request::handle_cloned))) - .route( - "/cloned-stream", - get(handler!(request::handle_cloned_stream)), - ) - .route("/cloned-fetch", get(handler!(fetch::handle_cloned_fetch))) - .route( - "/cloned-response", - get(handler!(fetch::handle_cloned_response_attributes)), - ) - .route("/wait/:delay", get(handler!(request::handle_wait_delay))) - .route( - "/custom-response-body", - get(handler!(request::handle_custom_response_body)), - ) - .route("/init-called", get(handler!(handle_init_called))) - .route("/cache-example", get(handler!(cache::handle_cache_example))) - .route( - "/cache-api/get/:key", - get(handler!(cache::handle_cache_api_get)), - ) - .route( - "/cache-api/put/:key", - put(handler!(cache::handle_cache_api_put)), - ) - .route( - "/cache-api/delete/:key", - post(handler!(cache::handle_cache_api_delete)), - ) - .route("/cache-stream", get(handler!(cache::handle_cache_stream))) - .route( - "/remote-by-request", - get(handler!(service::handle_remote_by_request)), - ) - .route( - "/remote-by-path", - get(handler!(service::handle_remote_by_path)), - ) - .route("/queue/send/:id", post(handler!(queue::handle_queue_send))) - .route( - "/queue/send_batch", - post(handler!(queue::handle_batch_send)), - ) - .route("/queue", get(handler!(queue::handle_queue))) - .route("/d1/prepared", get(handler!(d1::prepared_statement))) - .route("/d1/batch", get(handler!(d1::batch))) - .route("/d1/dump", get(handler!(d1::dump))) - .route("/d1/exec", post(handler!(d1::exec))) - .route("/d1/error", get(handler!(d1::error))) - .route("/kv/get", get(handler!(kv::get))) - .route("/kv/get-not-found", get(handler!(kv::get_not_found))) - .route("/kv/list-keys", get(handler!(kv::list_keys))) - .route("/kv/put-simple", get(handler!(kv::put_simple))) - .route("/kv/put-metadata", get(handler!(kv::put_metadata))) - .route( - "/kv/put-metadata-struct", - get(handler!(kv::put_metadata_struct)), - ) - .route("/kv/put-expiration", get(handler!(kv::put_expiration))) - .route("/r2/list-empty", get(handler!(r2::list_empty))) - .route("/r2/list", get(handler!(r2::list))) - .route("/r2/get-empty", get(handler!(r2::get_empty))) - .route("/r2/get", get(handler!(r2::get))) - .route("/r2/put", put(handler!(r2::put))) - .route("/r2/put-properties", put(handler!(r2::put_properties))) - .route("/r2/put-multipart", put(handler!(r2::put_multipart))) - .route("/r2/delete", delete(handler!(r2::delete))) - .route( - "/socket/failed", - get(handler!(socket::handle_socket_failed)), - ) - .route("/socket/read", get(handler!(socket::handle_socket_read))) - .fallback(get(handler!(catchall))) - .layer(Extension(env)) - .layer(Extension(data)) -} - -#[cfg(not(feature = "http"))] -pub fn make_router<'a>(data: SomeSharedData) -> Router<'a, SomeSharedData> { - Router::with_data(data) - .get("/request", handler_sync!(request::handle_a_request)) // can pass a fn pointer to keep routes tidy - .get_async("/async-request", handler!(request::handle_async_request)) - .get_async("/websocket", handler!(ws::handle_websocket)) - .get_async("/got-close-event", handler!(handle_close_event)) - .get_async("/ws-client", handler!(ws::handle_websocket_client)) - .get_async("/test-data", handler!(request::handle_test_data)) - .post_async("/xor/:num", handler!(request::handle_xor)) - .post_async("/headers", handler!(request::handle_headers)) - .post_async("/formdata-name", handler!(form::handle_formdata_name)) - .post_async("/is-secret", handler!(form::handle_is_secret)) - .post_async( - "/formdata-file-size", - handler!(form::handle_formdata_file_size), - ) - .get_async( - "/formdata-file-size/:hash", - handler!(form::handle_formdata_file_size_hash), - ) - .post_async("/post-file-size", handler!(request::handle_post_file_size)) - .get_async("/user/:id/test", handler!(user::handle_user_id_test)) - .get_async("/user/:id", handler!(user::handle_user_id)) - .post_async( - "/account/:id/zones", - handler!(user::handle_post_account_id_zones), - ) - .get_async( - "/account/:id/zones", - handler!(user::handle_get_account_id_zones), - ) - .post_async( - "/async-text-echo", - handler!(request::handle_async_text_echo), - ) - .get_async("/fetch", handler!(fetch::handle_fetch)) - .get_async("/fetch_json", handler!(fetch::handle_fetch_json)) - .get_async("/proxy_request/*url", handler!(fetch::handle_proxy_request)) - .get_async("/durable/alarm", handler!(alarm::handle_alarm)) - .get_async("/durable/:id", handler!(alarm::handle_id)) - .get_async("/durable/put-raw", handler!(alarm::handle_put_raw)) - .get_async("/durable/websocket", handler!(alarm::handle_websocket)) - .get_async("/secret", handler!(request::handle_secret)) - .get_async("/var", handler!(request::handle_var)) - .post_async("/kv/:key/:value", handler!(kv::handle_post_key_value)) - .get_async("/bytes", handler!(request::handle_bytes)) - .post_async("/api-data", handler!(request::handle_api_data)) - .post_async( - "/nonsense-repeat", - handler!(request::handle_nonsense_repeat), - ) - .get_async("/status/:code", handler!(request::handle_status)) - .put("/", handler_sync!(respond)) - .patch("/", handler_sync!(respond)) - .delete("/", handler_sync!(respond)) - .head("/", handler_sync!(respond)) - .put_async("/async", handler!(respond_async)) - .patch_async("/async", handler!(respond_async)) - .delete_async("/async", handler!(respond_async)) - .head_async("/async", handler!(respond_async)) - .options_async("/*catchall", handler!(handle_options_catchall)) - .get_async( - "/request-init-fetch", - handler!(fetch::handle_request_init_fetch), - ) - .get_async( - "/request-init-fetch-post", - handler!(fetch::handle_request_init_fetch_post), - ) - .get_async("/cancelled-fetch", handler!(fetch::handle_cancelled_fetch)) - .get_async("/fetch-timeout", handler!(fetch::handle_fetch_timeout)) - .get_async( - "/redirect-default", - handler!(request::handle_redirect_default), - ) - .get_async("/redirect-307", handler!(request::handle_redirect_307)) - .get_async("/now", handler!(request::handle_now)) - .get_async("/cloned", handler!(request::handle_cloned)) - .get_async("/cloned-stream", handler!(request::handle_cloned_stream)) - .get_async("/cloned-fetch", handler!(fetch::handle_cloned_fetch)) - .get_async( - "/cloned-response", - handler!(fetch::handle_cloned_response_attributes), - ) - .get_async("/wait/:delay", handler!(request::handle_wait_delay)) - .get_async( - "/custom-response-body", - handler!(request::handle_custom_response_body), - ) - .get_async("/init-called", handler!(handle_init_called)) - .get_async("/cache-example", handler!(cache::handle_cache_example)) - .get_async("/cache-api/get/:key", handler!(cache::handle_cache_api_get)) - .put_async("/cache-api/put/:key", handler!(cache::handle_cache_api_put)) - .post_async( - "/cache-api/delete/:key", - handler!(cache::handle_cache_api_delete), - ) - .get_async("/cache-stream", handler!(cache::handle_cache_stream)) - .get_async( - "/remote-by-request", - handler!(service::handle_remote_by_request), - ) - .get_async("/remote-by-path", handler!(service::handle_remote_by_path)) - .post_async("/queue/send/:id", handler!(queue::handle_queue_send)) - .post_async("/queue/send_batch", handler!(queue::handle_batch_send)) - .get_async("/queue", handler!(queue::handle_queue)) - .get_async("/d1/prepared", handler!(d1::prepared_statement)) - .get_async("/d1/batch", handler!(d1::batch)) - .get_async("/d1/dump", handler!(d1::dump)) - .post_async("/d1/exec", handler!(d1::exec)) - .get_async("/d1/error", handler!(d1::error)) - .get_async("/kv/get", handler!(kv::get)) - .get_async("/kv/get-not-found", handler!(kv::get_not_found)) - .get_async("/kv/list-keys", handler!(kv::list_keys)) - .get_async("/kv/put-simple", handler!(kv::put_simple)) - .get_async("/kv/put-metadata", handler!(kv::put_metadata)) - .get_async("/kv/put-metadata-struct", handler!(kv::put_metadata_struct)) - .get_async("/kv/put-expiration", handler!(kv::put_expiration)) - .get_async("/r2/list-empty", handler!(r2::list_empty)) - .get_async("/r2/list", handler!(r2::list)) - .get_async("/r2/get-empty", handler!(r2::get_empty)) - .get_async("/r2/get", handler!(r2::get)) - .put_async("/r2/put", handler!(r2::put)) - .put_async("/r2/put-properties", handler!(r2::put_properties)) - .put_async("/r2/put-multipart", handler!(r2::put_multipart)) - .delete_async("/r2/delete", handler!(r2::delete)) - .get_async("/socket/failed", handler!(socket::handle_socket_failed)) - .get_async("/socket/read", handler!(socket::handle_socket_read)) - .or_else_any_method_async("/*catchall", handler!(catchall)) -} - -fn respond(req: Request, _env: Env, _data: SomeSharedData) -> Result { - ResponseBuilder::new() - .with_header("x-testing", "123")? - .ok(format!("Ok: {}", String::from(req.method()))) -} - -async fn respond_async(req: Request, _env: Env, _data: SomeSharedData) -> Result { - ResponseBuilder::new() - .with_header("x-testing", "123")? - .ok(format!("Ok (async): {}", String::from(req.method()))) -} - -#[worker::send] -async fn handle_close_event(_req: Request, env: Env, _data: SomeSharedData) -> Result { - let some_namespace_kv = env.kv("SOME_NAMESPACE")?; - let got_close_event = some_namespace_kv - .get("got-close-event") - .text() - .await? - .unwrap_or_else(|| "false".into()); - - // Let the integration tests have some way of knowing if we successfully received the closed event. - Response::ok(got_close_event) -} - -#[worker::send] -async fn catchall(req: Request, _env: Env, _data: SomeSharedData) -> Result { - let uri = req.url()?; - let path = uri.path(); - console_log!("[or_else_any_method_async] caught: {}", path); - - let (builder, body) = Fetch::Url("https://github.com/404".parse().unwrap()) - .send() - .await? - .into_parts(); - - Ok(builder.with_status(404).body(body)) -} - -async fn handle_options_catchall( - req: Request, - _env: Env, - _data: SomeSharedData, -) -> Result { - let uri = req.url()?; - let path = uri.path(); - Response::ok(path) -} - -async fn handle_init_called(_req: Request, _env: Env, _data: SomeSharedData) -> Result { - let init_called = GLOBAL_STATE.load(Ordering::SeqCst); - Response::ok(init_called.to_string()) -} diff --git a/worker-sandbox/src/test/durable.rs b/worker-sandbox/src/test/durable.rs deleted file mode 100644 index d01e1619..00000000 --- a/worker-sandbox/src/test/durable.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::ensure; -use worker::*; - -#[allow(dead_code)] -pub async fn basic_test(env: &Env) -> Result<()> { - let namespace: ObjectNamespace = env.durable_object("MY_CLASS")?; - let id = namespace.id_from_name("A")?; - let bad = env.durable_object("DFSDF_FAKE_BINDING"); - ensure!(bad.is_err(), "Invalid binding did not raise error"); - - let stub = id.get_stub()?; - let res = stub.fetch_with_str("hello").await?.text().await?; - let res2 = stub - .fetch_with_request(Request::new_with_init( - "hello", - RequestInit::new() - .with_body(Some("lol".into())) - .with_method(Method::Post), - )?) - .await? - .text() - .await?; - - ensure!(res == res2, "Durable object responded wrong to 'hello'"); - - let res = stub.fetch_with_str("storage").await?.text().await?; - let num = res - .parse::() - .map_err(|_| "Durable Object responded wrong to 'storage': ".to_string() + &res)?; - let res = stub.fetch_with_str("storage").await?.text().await?; - let num2 = res - .parse::() - .map_err(|_| "Durable Object responded wrong to 'storage'".to_string())?; - - ensure!( - num2 == num + 1, - "Durable object responded wrong to 'storage'" - ); - - let res = stub.fetch_with_str("transaction").await?.text().await?; - let num = res - .parse::() - .map_err(|_| "Durable Object responded wrong to 'transaction': ".to_string() + &res)?; - - ensure!( - num == num2 + 1, - "Durable object responded wrong to 'storage'" - ); - - Ok(()) -} diff --git a/worker-sandbox/src/test/export_durable_object.rs b/worker-sandbox/src/test/export_durable_object.rs deleted file mode 100644 index efd7cb11..00000000 --- a/worker-sandbox/src/test/export_durable_object.rs +++ /dev/null @@ -1,136 +0,0 @@ -use serde::Serialize; -use std::collections::HashMap; - -use worker::{js_sys::Uint8Array, wasm_bindgen::JsValue, *}; - -use crate::ensure; - -#[durable_object] -pub struct MyClass { - state: State, - number: usize, -} - -#[durable_object] -impl DurableObject for MyClass { - fn new(state: State, _env: Env) -> Self { - Self { state, number: 0 } - } - - async fn fetch(&mut self, req: Request) -> Result { - let handler = async move { - match req.path().as_str() { - "/hello" => Response::ok("Hello!"), - "/storage" => { - let mut storage = self.state.storage(); - let map = [("one".to_string(), 1), ("two".to_string(), 2)] - .iter() - .cloned() - .collect::>(); - storage.put("map", map.clone()).await?; - storage.put("array", [("one", 1), ("two", 2)]).await?; - storage.put("anything", Some(45)).await?; - - let list = storage.list().await?; - let mut keys = vec![]; - - for key in list.keys() { - let key = key? - .as_string() - .ok_or_else(|| "Key wasn't a string".to_string())?; - keys.push(key); - } - - ensure!( - keys == vec!["anything", "array", "map"], - format!("Didn't list all of the keys: {keys:?}") - ); - let vals = storage - .get_multiple(keys) - .await - .map_err(|e| e.to_string() + " -- get_multiple")?; - ensure!( - serde_wasm_bindgen::from_value::>( - vals.get(&"anything".into()) - )? == Some(45), - "Didn't get the right Option using get_multiple" - ); - ensure!( - serde_wasm_bindgen::from_value::<[(String, i32); 2]>( - vals.get(&"array".into()) - )? == [("one".to_string(), 1), ("two".to_string(), 2)], - "Didn't get the right array using get_multiple" - ); - ensure!( - serde_wasm_bindgen::from_value::>( - vals.get(&"map".into()) - )? == map, - "Didn't get the right HashMap using get_multiple" - ); - - { - let bytes = Uint8Array::new_with_length(3); - bytes.copy_from(b"123"); - storage.put_raw("bytes", bytes).await?; - let bytes = storage.get::>("bytes").await?; - storage.delete("bytes").await?; - ensure!( - bytes == b"123", - "eficient serialization of bytes is not preserved" - ); - } - - #[derive(Serialize)] - struct Stuff { - thing: String, - other: i32, - } - storage - .put_multiple(Stuff { - thing: "Hello there".to_string(), - other: 56, - }) - .await?; - - ensure!( - storage.get::("thing").await? == "Hello there", - "Didn't put the right thing with put_multiple" - ); - ensure!( - storage.get::("other").await? == 56, - "Didn't put the right thing with put_multiple" - ); - - storage.delete_multiple(vec!["thing", "other"]).await?; - - { - let obj = js_sys::Object::new(); - const BAR: &[u8] = b"bar"; - let value = Uint8Array::new_with_length(BAR.len() as _); - value.copy_from(BAR); - js_sys::Reflect::set(&obj, &JsValue::from_str("foo"), &value.into())?; - storage.put_multiple_raw(obj).await?; - ensure!( - storage.get::>("foo").await? == BAR, - "Didn't the right thing with put_multiple_raw" - ); - } - - self.number = storage.get("count").await.unwrap_or(0) + 1; - - storage.delete_all().await?; - - storage.put("count", self.number).await?; - Response::ok(self.number.to_string()) - } - "/transaction" => { - Response::error("transactional storage API is still unstable", 501) - } - _ => Response::error("Not Found", 404), - } - }; - handler - .await - .or_else(|err| Response::error(err.to_string(), 500)) - } -} diff --git a/worker-sandbox/src/test/mod.rs b/worker-sandbox/src/test/mod.rs deleted file mode 100644 index ec112528..00000000 --- a/worker-sandbox/src/test/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod durable; -pub mod export_durable_object; -pub mod put_raw; - -#[macro_export] -macro_rules! ensure { - ($ex:expr, $er:expr) => { - if !$ex { - return Err($er.into()); - } - }; -} diff --git a/worker-sandbox/src/utils.rs b/worker-sandbox/src/utils.rs deleted file mode 100644 index aab768f6..00000000 --- a/worker-sandbox/src/utils.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cfg_if::cfg_if; - -cfg_if! { - // https://github.com/rustwasm/console_error_panic_hook#readme - if #[cfg(feature = "console_error_panic_hook")] { - extern crate console_error_panic_hook; - pub use self::console_error_panic_hook::set_once as set_panic_hook; - } else { - #[inline] - pub fn set_panic_hook() {} - } -} diff --git a/worker-sandbox/tests/d1.spec.ts b/worker-sandbox/tests/d1.spec.ts deleted file mode 100644 index 71a706c9..00000000 --- a/worker-sandbox/tests/d1.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { describe, test, expect } from "vitest"; -import { mf } from "./mf"; - -async function exec(query: string): Promise { - const resp = await mf.dispatchFetch("http://fake.host/d1/exec", { - method: "POST", - body: query.split("\n").join(""), - }); - - const body = await resp.text(); - expect(resp.status).toBe(200); - return Number(body); -} - -describe("d1", () => { - test("create table", async () => { - const query = `CREATE TABLE IF NOT EXISTS uniqueTable ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - age INTEGER NOT NULL - );`; - - expect(await exec(query)).toBe(1); - }); - - test("insert data", async () => { - let query = `CREATE TABLE IF NOT EXISTS people ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - age INTEGER NOT NULL - );`; - - expect(await exec(query)).toBe(1); - - query = `INSERT OR IGNORE INTO people - (id, name, age) - VALUES - (1, 'Freddie Pearce', 26), - (2, 'Wynne Ogley', 67), - (3, 'Dorian Fischer', 19), - (4, 'John Smith', 92), - (5, 'Magaret Willamson', 54), - (6, 'Ryan Upton', 21);`; - - expect(await exec(query)).toBe(1); - }); - - test("prepared statement", async () => { - const resp = await mf.dispatchFetch("http://fake.host/d1/prepared"); - expect(resp.status).toBe(200); - }); - - test("batch", async () => { - const resp = await mf.dispatchFetch("http://fake.host/d1/batch"); - expect(resp.status).toBe(200); - }); - - test("error", async () => { - const resp = await mf.dispatchFetch("http://fake.host/d1/error"); - expect(resp.status).toBe(200); - }); -}); diff --git a/worker-sandbox/tests/durable.spec.ts b/worker-sandbox/tests/durable.spec.ts deleted file mode 100644 index b7ad20ea..00000000 --- a/worker-sandbox/tests/durable.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {describe, test, expect, vi} from "vitest"; -import { mf } from "./mf"; -import {MessageEvent} from "miniflare"; - -describe("durable", () => { - test("put-raw", async () => { - const resp = await mf.dispatchFetch("https://fake.host/durable/put-raw"); - expect(await resp.text()).toBe("ok"); - }); - - test("websocket-to-durable", async () => { - const resp = await mf.dispatchFetch("http://fake.host/durable/websocket", { - headers: { - upgrade: "websocket", - }, - }); - expect(resp.webSocket).not.toBeNull(); - - const socket = resp.webSocket!; - socket.accept(); - - const handlers = { - messageHandler: (event: MessageEvent) => { - expect(event.data).toMatch(/^10|20|30$/); - }, - close(event: CloseEvent) {}, - }; - - const messageHandlerWrapper = vi.spyOn(handlers, "messageHandler"); - const closeHandlerWrapper = vi.spyOn(handlers, "messageHandler"); - socket.addEventListener("message", handlers.messageHandler); - socket.addEventListener("close", handlers.close); - - socket.send("hi, can you ++?"); - await new Promise((resolve) => setTimeout(resolve, 500)); - expect(messageHandlerWrapper).toHaveBeenCalledTimes(1); - - socket.send("hi again, more ++?"); - await new Promise((resolve) => setTimeout(resolve, 500)); - expect(messageHandlerWrapper).toHaveBeenCalledTimes(2); - - socket.close(); - expect(closeHandlerWrapper).toBeCalled(); - }); -}); - diff --git a/worker-sandbox/tests/mf.ts b/worker-sandbox/tests/mf.ts deleted file mode 100644 index bd5b9fb7..00000000 --- a/worker-sandbox/tests/mf.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Miniflare, Response } from "miniflare"; -import { MockAgent } from "undici"; - -const mockAgent = new MockAgent(); - -mockAgent - .get("https://cloudflare.com/") - .intercept({ path: "/" }) - .reply(200, "cloudflare!"); - -mockAgent - .get("https://miniflare.mocks/") - .intercept({ path: "/delay" }) - .reply(200, "cloudflare!") - .delay(10000); - -mockAgent - .get("https://jsonplaceholder.typicode.com/") - .intercept({ path: "/todos/1" }) - .reply( - 200, - { - userId: 1, - id: 1, - title: "delectus aut autem", - completed: false, - }, - { - headers: { - "content-type": "application/json", - }, - } - ); - -export const mf = new Miniflare({ - scriptPath: "./build/worker/shim.mjs", - compatibilityDate: "2023-05-18", - cache: true, - cachePersist: false, - d1Databases: ["DB"], - d1Persist: false, - kvPersist: false, - r2Persist: false, - modules: true, - modulesRules: [ - { type: "CompiledWasm", include: ["**/*.wasm"], fallthrough: true }, - ], - bindings: { - EXAMPLE_SECRET: "example", - SOME_SECRET: "secret!", - }, - durableObjects: { - COUNTER: "Counter", - PUT_RAW_TEST_OBJECT: "PutRawTestObject", - }, - kvNamespaces: ["SOME_NAMESPACE", "FILE_SIZES", "TEST"], - serviceBindings: { - async remote() { - return new Response("hello world"); - }, - }, - r2Buckets: ["EMPTY_BUCKET", "PUT_BUCKET", "SEEDED_BUCKET", "DELETE_BUCKET"], - queueConsumers: { - my_queue: { - maxBatchTimeout: 1, - }, - }, - queueProducers: ["my_queue", "my_queue"], - fetchMock: mockAgent, -}); diff --git a/worker-sandbox/tests/websocket.spec.ts b/worker-sandbox/tests/websocket.spec.ts deleted file mode 100644 index 95e49b78..00000000 --- a/worker-sandbox/tests/websocket.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, test, vi } from "vitest"; -import { MessageEvent } from "miniflare"; -import { mf } from "./mf"; - -describe("websocket", () => { - test("to echo", async () => { - const resp = await mf.dispatchFetch("http://fake.host/websocket", { - headers: { - upgrade: "websocket", - }, - }); - expect(resp.webSocket).not.toBeNull(); - - const socket = resp.webSocket!; - socket.accept(); - - const handlers = { - messageHandler: (event: MessageEvent) => - expect(event.data).toBe("Hello, world!"), - close(event: CloseEvent) {}, - }; - - const messageHandlerWrapper = vi.spyOn(handlers, "messageHandler"); - const closeHandlerWrapper = vi.spyOn(handlers, "messageHandler"); - socket.addEventListener("message", handlers.messageHandler); - socket.addEventListener("close", handlers.close); - - socket.send("Hello, world!"); - - await new Promise((resolve) => setTimeout(resolve, 500)); - - expect(messageHandlerWrapper).toBeCalled(); - socket.close(); - expect(closeHandlerWrapper).toBeCalled(); - }); -}); diff --git a/worker-sandbox/wrangler.toml b/worker-sandbox/wrangler.toml deleted file mode 100644 index 017b7ce0..00000000 --- a/worker-sandbox/wrangler.toml +++ /dev/null @@ -1,70 +0,0 @@ -name = "testing-rust-worker" -type = "javascript" -workers_dev = true -compatibility_date = "2022-09-12" # required -compatibility_flags = ["streams_enable_constructors"] - -kv_namespaces = [ - { binding = "SOME_NAMESPACE", id = "SOME_NAMESPACE", preview_id = "SOME_NAMESPACE" }, - { binding = "FILE_SIZES", id = "FILE_SIZES", preview_id = "FILE_SIZES" }, -] - -vars = { SOME_VARIABLE = "some value" } - -[[services]] -binding = "remote" -service = "remote-service" - -[miniflare.mounts] -remote-service = "./remote-service" - -[durable_objects] -bindings = [ - { name = "COUNTER", class_name = "Counter" }, - { name = "ALARM", class_name = "AlarmObject" }, - { name = "PUT_RAW_TEST_OBJECT", class_name = "PutRawTestObject" }, -] - -[[d1_databases]] -binding = 'DB' -database_name = 'my_db' -database_id = 'test' -preview_database_id = 'preview-test' - -[[queues.consumers]] -queue = "my_queue" - -[[queues.producers]] -queue = "my_queue" -binding = "my_queue" -[[r2_buckets]] -binding = 'EMPTY_BUCKET' -bucket_name = 'empty_bucket' -preview_bucket_name = 'empty_bucket' - -[[r2_buckets]] -binding = 'PUT_BUCKET' -bucket_name = 'put_bucket' -preview_bucket_name = 'put_bucket' - -[[r2_buckets]] -binding = 'SEEDED_BUCKET' -bucket_name = 'seeded_bucket' -preview_bucket_name = 'seeded_bucket' - -[[r2_buckets]] -binding = 'DELETE_BUCKET' -bucket_name = 'delete_bucket' -preview_bucket_name = 'delete_bucket' - -[build] -command = "worker-build --release" - -[build.upload] -dir = "build/worker" -format = "modules" -main = "./shim.mjs" - -[[build.upload.rules]] -globs = ["**/*.wasm"] -type = "CompiledWasm" diff --git a/worker-sys/Cargo.toml b/worker-sys/Cargo.toml index b6502898..8de578dd 100644 --- a/worker-sys/Cargo.toml +++ b/worker-sys/Cargo.toml @@ -2,7 +2,7 @@ name = "worker-sys" authors = ["Cloudflare Workers Team "] edition = "2018" -version = "0.3.1" +version = "0.6.7" license = "Apache-2.0" repository = "https://github.com/cloudflare/workers-rs/tree/main/worker-sys" description = "Low-level extern definitions / FFI bindings to the Cloudflare Workers JS Runtime." @@ -10,10 +10,10 @@ description = "Low-level extern definitions / FFI bindings to the Cloudflare Wor [dependencies] js-sys.workspace = true wasm-bindgen.workspace = true -cfg-if = "1.0.0" +cfg-if = "1.0.1" [dependencies.web-sys] -version = "0.3.63" +version = "0.3.70" features = [ "ReadableStream", "WritableStream", diff --git a/worker-sys/src/ext.rs b/worker-sys/src/ext.rs index 014731af..2e68d5f3 100644 --- a/worker-sys/src/ext.rs +++ b/worker-sys/src/ext.rs @@ -1,5 +1,4 @@ mod abort_controller; -mod abort_signal; mod cache_storage; mod headers; mod request; @@ -8,9 +7,8 @@ mod response_init; mod websocket; pub use abort_controller::*; -pub use abort_signal::*; pub use cache_storage::*; -pub use headers::*; +pub use headers::HeadersExt; pub use request::*; pub use response::*; pub use response_init::*; diff --git a/worker-sys/src/ext/abort_signal.rs b/worker-sys/src/ext/abort_signal.rs deleted file mode 100644 index e88e6e49..00000000 --- a/worker-sys/src/ext/abort_signal.rs +++ /dev/null @@ -1,42 +0,0 @@ -use wasm_bindgen::prelude::*; - -mod glue { - use super::*; - - #[wasm_bindgen] - extern "C" { - #[wasm_bindgen] - pub type AbortSignal; - - #[wasm_bindgen(method, catch, getter)] - pub fn reason(this: &AbortSignal) -> Result; - - #[wasm_bindgen(static_method_of=AbortSignal, catch)] - pub fn abort() -> Result; - - #[wasm_bindgen(static_method_of=AbortSignal, js_name=abort, catch)] - pub fn abort_with_reason(reason: &JsValue) -> Result; - } -} - -pub trait AbortSignalExt { - fn reason(&self) -> JsValue; - - fn abort() -> web_sys::AbortSignal; - - fn abort_with_reason(reason: &JsValue) -> web_sys::AbortSignal; -} - -impl AbortSignalExt for web_sys::AbortSignal { - fn reason(&self) -> JsValue { - self.unchecked_ref::().reason().unwrap() - } - - fn abort() -> web_sys::AbortSignal { - glue::AbortSignal::abort().unwrap() - } - - fn abort_with_reason(reason: &JsValue) -> web_sys::AbortSignal { - glue::AbortSignal::abort_with_reason(reason).unwrap() - } -} diff --git a/worker-sys/src/ext/headers.rs b/worker-sys/src/ext/headers.rs index ebed105f..c4bc55ab 100644 --- a/worker-sys/src/ext/headers.rs +++ b/worker-sys/src/ext/headers.rs @@ -5,38 +5,20 @@ mod glue { #[wasm_bindgen] extern "C" { - #[wasm_bindgen(extends=js_sys::Object)] + #[wasm_bindgen(extends = js_sys::Object)] pub type Headers; - #[wasm_bindgen(method, catch)] - pub fn entries(this: &Headers) -> Result; - - #[wasm_bindgen(method, catch)] - pub fn keys(this: &Headers) -> Result; - - #[wasm_bindgen(method, catch)] - pub fn values(this: &Headers) -> Result; + #[wasm_bindgen(method, js_name = getAll)] + pub fn get_all(this: &Headers, name: &str) -> js_sys::Array; } } pub trait HeadersExt { - fn entries(&self) -> Result; - - fn keys(&self) -> Result; - - fn values(&self) -> Result; + fn get_all(&self, name: &str) -> js_sys::Array; } impl HeadersExt for web_sys::Headers { - fn entries(&self) -> Result { - self.unchecked_ref::().entries() - } - - fn keys(&self) -> Result { - self.unchecked_ref::().keys() - } - - fn values(&self) -> Result { - self.unchecked_ref::().values() + fn get_all(&self, name: &str) -> js_sys::Array { + self.unchecked_ref::().get_all(name) } } diff --git a/worker-sys/src/types.rs b/worker-sys/src/types.rs index 76235254..d6e63d4d 100644 --- a/worker-sys/src/types.rs +++ b/worker-sys/src/types.rs @@ -1,3 +1,5 @@ +mod ai; +mod analytics_engine; mod context; mod crypto; #[cfg(feature = "d1")] @@ -11,12 +13,17 @@ mod incoming_request_cf_properties; #[cfg(feature = "queue")] mod queue; mod r2; +mod rate_limit; mod schedule; +mod secret_store; mod socket; mod tls_client_auth; mod version; mod websocket_pair; +mod websocket_request_response_pair; +pub use ai::*; +pub use analytics_engine::*; pub use context::*; pub use crypto::*; #[cfg(feature = "d1")] @@ -30,8 +37,11 @@ pub use incoming_request_cf_properties::*; #[cfg(feature = "queue")] pub use queue::*; pub use r2::*; +pub use rate_limit::*; pub use schedule::*; +pub use secret_store::*; pub use socket::*; pub use tls_client_auth::*; pub use version::*; pub use websocket_pair::*; +pub use websocket_request_response_pair::*; diff --git a/worker-sys/src/types/ai.rs b/worker-sys/src/types/ai.rs new file mode 100644 index 00000000..f362138b --- /dev/null +++ b/worker-sys/src/types/ai.rs @@ -0,0 +1,12 @@ +use js_sys::Promise; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends=::js_sys::Object, js_name=Ai)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type Ai; + + #[wasm_bindgen(structural, method, js_class=Ai, js_name=run)] + pub fn run(this: &Ai, model: &str, input: JsValue) -> Promise; +} diff --git a/worker-sys/src/types/analytics_engine.rs b/worker-sys/src/types/analytics_engine.rs new file mode 100644 index 00000000..35f4d1e4 --- /dev/null +++ b/worker-sys/src/types/analytics_engine.rs @@ -0,0 +1,11 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends=js_sys::Object, js_name=AnalyticsEngineDataset)] + #[derive(Debug, Clone)] + pub type AnalyticsEngineDataset; + + #[wasm_bindgen(method, catch, js_name=writeDataPoint)] + pub fn write_data_point(this: &AnalyticsEngineDataset, event: JsValue) -> Result<(), JsValue>; +} diff --git a/worker-sys/src/types/context.rs b/worker-sys/src/types/context.rs index 63f7eebb..e6f05dc5 100644 --- a/worker-sys/src/types/context.rs +++ b/worker-sys/src/types/context.rs @@ -11,4 +11,7 @@ extern "C" { #[wasm_bindgen(method, catch, js_name=passThroughOnException)] pub fn pass_through_on_exception(this: &Context) -> Result<(), JsValue>; + + #[wasm_bindgen(method, getter)] + pub fn props(this: &Context) -> JsValue; } diff --git a/worker-sys/src/types/crypto.rs b/worker-sys/src/types/crypto.rs index 62026fac..417c0e83 100644 --- a/worker-sys/src/types/crypto.rs +++ b/worker-sys/src/types/crypto.rs @@ -5,6 +5,7 @@ use web_sys::WritableStream; extern "C" { /// Bindings for the non-standard [crypto.DigestStream](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#constructors) API #[wasm_bindgen(extends = WritableStream)] + #[derive(Debug)] pub type DigestStream; #[wasm_bindgen(constructor, js_namespace = crypto)] diff --git a/worker-sys/src/types/durable_object.rs b/worker-sys/src/types/durable_object.rs index 04f8503c..c1d3b270 100644 --- a/worker-sys/src/types/durable_object.rs +++ b/worker-sys/src/types/durable_object.rs @@ -1,13 +1,17 @@ use wasm_bindgen::prelude::*; +mod container; mod id; mod namespace; +mod sql_storage; mod state; mod storage; mod transaction; +pub use container::*; pub use id::*; pub use namespace::*; +pub use sql_storage::*; pub use state::*; pub use storage::*; pub use transaction::*; @@ -15,7 +19,7 @@ pub use transaction::*; #[wasm_bindgen] extern "C" { #[wasm_bindgen(extends=js_sys::Object)] - #[derive(Clone)] + #[derive(Clone, Debug)] pub type DurableObject; #[wasm_bindgen(method, catch, js_name=fetch)] diff --git a/worker-sys/src/types/durable_object/container.rs b/worker-sys/src/types/durable_object/container.rs new file mode 100644 index 00000000..7446aed3 --- /dev/null +++ b/worker-sys/src/types/durable_object/container.rs @@ -0,0 +1,46 @@ +use js_sys::{Object, Promise}; +use wasm_bindgen::prelude::*; + +use crate::Fetcher; + +/// ```ts +/// interface Container { +/// get running(): boolean; +/// start(options?: ContainerStartupOptions): void; +/// monitor(): Promise; +/// destroy(error?: any): Promise; +/// signal(signo: number): void; +/// getTcpPort(port: number): Fetcher; +/// } +/// +/// interface ContainerStartupOptions { +/// entrypoint?: string[]; +/// enableInternet: boolean; +/// env?: Record; +/// } +/// ``` + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends=Object)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type Container; + + #[wasm_bindgen(method, getter)] + pub fn running(this: &Container) -> bool; + + #[wasm_bindgen(method, catch)] + pub fn start(this: &Container, options: &JsValue) -> Result<(), JsValue>; + + #[wasm_bindgen(method)] + pub fn monitor(this: &Container) -> Promise; + + #[wasm_bindgen(method)] + pub fn destroy(this: &Container, error: Option<&str>) -> Promise; + + #[wasm_bindgen(method, catch)] + pub fn signal(this: &Container, signo: i32) -> Result<(), JsValue>; + + #[wasm_bindgen(method, catch, js_name=getTcpPort)] + pub fn get_tcp_port(this: &Container, port: u16) -> Result; +} diff --git a/worker-sys/src/types/durable_object/id.rs b/worker-sys/src/types/durable_object/id.rs index 9a4dcb28..d9801931 100644 --- a/worker-sys/src/types/durable_object/id.rs +++ b/worker-sys/src/types/durable_object/id.rs @@ -7,4 +7,18 @@ extern "C" { #[wasm_bindgen(method, catch, js_name=toString)] pub fn to_string(this: &DurableObjectId) -> Result; + + #[wasm_bindgen(method)] + pub fn equals(this: &DurableObjectId, other: &DurableObjectId) -> bool; + + #[wasm_bindgen(method, getter)] + pub fn name(this: &DurableObjectId) -> Option; +} + +impl core::fmt::Debug for DurableObjectId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DurableObjectId") + .field("name", &self.name()) + .finish() + } } diff --git a/worker-sys/src/types/durable_object/namespace.rs b/worker-sys/src/types/durable_object/namespace.rs index e7ba7600..cca4e988 100644 --- a/worker-sys/src/types/durable_object/namespace.rs +++ b/worker-sys/src/types/durable_object/namespace.rs @@ -5,7 +5,7 @@ use crate::types::{DurableObject, DurableObjectId}; #[wasm_bindgen] extern "C" { #[wasm_bindgen(extends=js_sys::Object)] - #[derive(Clone)] + #[derive(Debug, Clone)] pub type DurableObjectNamespace; #[wasm_bindgen(method, catch, js_name=idFromName)] @@ -34,4 +34,22 @@ extern "C" { this: &DurableObjectNamespace, id: &DurableObjectId, ) -> Result; + + #[wasm_bindgen(method, catch, js_name=get)] + pub fn get_with_options( + this: &DurableObjectNamespace, + id: &DurableObjectId, + options: &JsValue, + ) -> Result; + + #[wasm_bindgen(method, catch, js_name=getByName)] + pub fn get_by_name(this: &DurableObjectNamespace, name: &str) + -> Result; + + #[wasm_bindgen(method, catch, js_name=getByName)] + pub fn get_by_name_with_options( + this: &DurableObjectNamespace, + name: &str, + options: &JsValue, + ) -> Result; } diff --git a/worker-sys/src/types/durable_object/sql_storage.rs b/worker-sys/src/types/durable_object/sql_storage.rs new file mode 100644 index 00000000..fb9cbe25 --- /dev/null +++ b/worker-sys/src/types/durable_object/sql_storage.rs @@ -0,0 +1,67 @@ +use js_sys::{Array, Iterator as JsIterator, Object}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = js_sys::Object)] + #[derive(Clone, Debug)] + pub type SqlStorage; + + /// Returns the on-disk size of the SQLite database, in bytes. + #[wasm_bindgen(method, getter, js_name = databaseSize)] + pub fn database_size(this: &SqlStorage) -> f64; + + /// Execute a SQL statement (optionally with bindings) and return a cursor over the results. + /// + /// The JavaScript definition of `exec` is variadic, taking a SQL string followed by an + /// arbitrary number of binding parameters. In Rust we accept the bindings packed into an + /// `Array` – callers can construct the array (or use the helper provided in the higher-level + /// `worker` crate). + #[wasm_bindgen(structural, method, catch, variadic, js_class = SqlStorage, js_name = exec)] + pub fn exec( + this: &SqlStorage, + query: &str, + bindings: Array, + ) -> Result; + + // TODO: prepare, ingest, and setMaxPageCountForTest are experimental + // https://github.com/cloudflare/workerd/blob/3d177d3/src/workerd/api/sql.h#L43-L52 +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = js_sys::Object)] + #[derive(Clone, Debug)] + pub type SqlStorageCursor; + + /// JavaScript `Iterator.next()` implementation. Returns an object with `{ done, value }`. + #[wasm_bindgen(method, js_name = next)] + pub fn next(this: &SqlStorageCursor) -> Object; + + /// Convert the remaining rows into an array of objects. + #[wasm_bindgen(method, js_name = toArray)] + pub fn to_array(this: &SqlStorageCursor) -> Array; + + /// Returns the single row if exactly one row exists, otherwise throws. + #[wasm_bindgen(method)] + pub fn one(this: &SqlStorageCursor) -> JsValue; + + /// Returns an iterator where each row is an array rather than an object. + #[wasm_bindgen(method)] + pub fn raw(this: &SqlStorageCursor) -> JsIterator; + + /// Column names in the order they appear in `raw()` row arrays. + #[wasm_bindgen(method, getter, js_name = columnNames)] + pub fn column_names(this: &SqlStorageCursor) -> Array; + + /// Rows read so far. + #[wasm_bindgen(method, getter, js_name = rowsRead)] + pub fn rows_read(this: &SqlStorageCursor) -> f64; + + /// Rows written so far. + #[wasm_bindgen(method, getter, js_name = rowsWritten)] + pub fn rows_written(this: &SqlStorageCursor) -> f64; + + // TODO: resusedCachedQueryForTest is experimental + // https://github.com/cloudflare/workerd/blob/3d177d3/src/workerd/api/sql.h#L219 +} diff --git a/worker-sys/src/types/durable_object/state.rs b/worker-sys/src/types/durable_object/state.rs index 9ecb79da..32e4dbf7 100644 --- a/worker-sys/src/types/durable_object/state.rs +++ b/worker-sys/src/types/durable_object/state.rs @@ -1,6 +1,8 @@ use wasm_bindgen::prelude::*; -use crate::types::{DurableObjectId, DurableObjectStorage}; +use crate::types::{ + Container, DurableObjectId, DurableObjectStorage, WebSocketRequestResponsePair, +}; #[wasm_bindgen] extern "C" { @@ -13,6 +15,9 @@ extern "C" { #[wasm_bindgen(method, catch, getter)] pub fn storage(this: &DurableObjectState) -> Result; + #[wasm_bindgen(method, getter)] + pub fn container(this: &DurableObjectState) -> Option; + #[wasm_bindgen(method, catch, js_name=waitUntil)] pub fn wait_until(this: &DurableObjectState, promise: &js_sys::Promise) -> Result<(), JsValue>; @@ -43,4 +48,23 @@ extern "C" { this: &DurableObjectState, ws: &web_sys::WebSocket, ) -> Result, JsValue>; + + #[wasm_bindgen(method, catch, js_name=setWebSocketAutoResponse)] + pub fn set_websocket_auto_response( + this: &DurableObjectState, + pair: &WebSocketRequestResponsePair, + ) -> Result<(), JsValue>; + + #[wasm_bindgen(method, catch, js_name=getWebSocketAutoResponse)] + pub fn get_websocket_auto_response( + this: &DurableObjectState, + ) -> Result, JsValue>; +} + +impl core::fmt::Debug for DurableObjectState { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("DurableObjectState") + .field("id", &self.id()) + .finish() + } } diff --git a/worker-sys/src/types/durable_object/storage.rs b/worker-sys/src/types/durable_object/storage.rs index 5d3692a9..08dadaa9 100644 --- a/worker-sys/src/types/durable_object/storage.rs +++ b/worker-sys/src/types/durable_object/storage.rs @@ -74,4 +74,7 @@ extern "C" { this: &DurableObjectStorage, options: js_sys::Object, ) -> Result; + + #[wasm_bindgen(method, getter)] + pub fn sql(this: &DurableObjectStorage) -> crate::types::SqlStorage; } diff --git a/worker-sys/src/types/durable_object/transaction.rs b/worker-sys/src/types/durable_object/transaction.rs index cccbe73d..6a6ca231 100644 --- a/worker-sys/src/types/durable_object/transaction.rs +++ b/worker-sys/src/types/durable_object/transaction.rs @@ -3,6 +3,7 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { #[wasm_bindgen(extends=js_sys::Object)] + #[derive(Debug)] pub type DurableObjectTransaction; #[wasm_bindgen(method, catch)] diff --git a/worker-sys/src/types/hyperdrive.rs b/worker-sys/src/types/hyperdrive.rs index ef74d695..296d326b 100644 --- a/worker-sys/src/types/hyperdrive.rs +++ b/worker-sys/src/types/hyperdrive.rs @@ -3,7 +3,7 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { #[wasm_bindgen(extends=js_sys::Object)] - #[derive(Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq)] pub type Hyperdrive; #[wasm_bindgen(method, getter, js_name=connectionString)] diff --git a/worker-sys/src/types/incoming_request_cf_properties.rs b/worker-sys/src/types/incoming_request_cf_properties.rs index 2d41beb2..b7044013 100644 --- a/worker-sys/src/types/incoming_request_cf_properties.rs +++ b/worker-sys/src/types/incoming_request_cf_properties.rs @@ -12,10 +12,10 @@ extern "C" { pub fn colo(this: &IncomingRequestCfProperties) -> Result; #[wasm_bindgen(method, catch, getter)] - pub fn asn(this: &IncomingRequestCfProperties) -> Result; + pub fn asn(this: &IncomingRequestCfProperties) -> Result, JsValue>; #[wasm_bindgen(method, catch, getter, js_name=asOrganization)] - pub fn as_organization(this: &IncomingRequestCfProperties) -> Result; + pub fn as_organization(this: &IncomingRequestCfProperties) -> Result, JsValue>; #[wasm_bindgen(method, catch, getter)] pub fn country(this: &IncomingRequestCfProperties) -> Result, JsValue>; @@ -61,6 +61,9 @@ extern "C" { #[wasm_bindgen(method, catch, getter, js_name=regionCode)] pub fn region_code(this: &IncomingRequestCfProperties) -> Result, JsValue>; + #[wasm_bindgen(method, catch, getter, js_name=hostMetadata)] + pub fn host_metadata(this: &IncomingRequestCfProperties) -> Result; + #[wasm_bindgen(method, catch, getter)] pub fn timezone(this: &IncomingRequestCfProperties) -> Result; diff --git a/worker-sys/src/types/r2/bucket.rs b/worker-sys/src/types/r2/bucket.rs index 1903b7f6..4cca6191 100644 --- a/worker-sys/src/types/r2/bucket.rs +++ b/worker-sys/src/types/r2/bucket.rs @@ -23,6 +23,10 @@ extern "C" { #[wasm_bindgen(method, catch)] pub fn delete(this: &R2Bucket, key: String) -> Result; + #[wasm_bindgen(method, catch, js_name=delete)] + pub fn delete_multiple(this: &R2Bucket, keys: Vec) + -> Result; + #[wasm_bindgen(method, catch)] pub fn list(this: &R2Bucket, options: JsValue) -> Result; diff --git a/worker-sys/src/types/r2/object.rs b/worker-sys/src/types/r2/object.rs index 7763f35c..425a5d9f 100644 --- a/worker-sys/src/types/r2/object.rs +++ b/worker-sys/src/types/r2/object.rs @@ -15,7 +15,7 @@ extern "C" { pub fn version(this: &R2Object) -> Result; #[wasm_bindgen(method, catch, getter)] - pub fn size(this: &R2Object) -> Result; + pub fn size(this: &R2Object) -> Result; #[wasm_bindgen(method, catch, getter)] pub fn etag(this: &R2Object) -> Result; diff --git a/worker-sys/src/types/rate_limit.rs b/worker-sys/src/types/rate_limit.rs new file mode 100644 index 00000000..f95db970 --- /dev/null +++ b/worker-sys/src/types/rate_limit.rs @@ -0,0 +1,12 @@ +use js_sys::Promise; +use wasm_bindgen::JsValue; + +#[wasm_bindgen::prelude::wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends=js_sys::Object)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type RateLimiter; + + #[wasm_bindgen(method, catch)] + pub fn limit(this: &RateLimiter, arg: js_sys::Object) -> Result; +} diff --git a/worker-sys/src/types/secret_store.rs b/worker-sys/src/types/secret_store.rs new file mode 100644 index 00000000..d3421ffe --- /dev/null +++ b/worker-sys/src/types/secret_store.rs @@ -0,0 +1,11 @@ +use wasm_bindgen::prelude::*; +#[wasm_bindgen] +extern "C" { + #[derive(Debug, Clone)] + #[wasm_bindgen(extends = js_sys::Object)] + pub type SecretStoreSys; + #[wasm_bindgen(method, catch, js_name = "get")] + pub fn get( + this: &SecretStoreSys, + ) -> std::result::Result; +} diff --git a/worker-sys/src/types/version.rs b/worker-sys/src/types/version.rs index a5beb6e4..3de62520 100644 --- a/worker-sys/src/types/version.rs +++ b/worker-sys/src/types/version.rs @@ -16,3 +16,13 @@ extern "C" { #[wasm_bindgen(method, getter, js_name=timestamp)] pub fn timestamp(this: &CfVersionMetadata) -> String; } + +impl core::fmt::Debug for CfVersionMetadata { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CfVersionMetadata") + .field("id", &self.id()) + .field("tag", &self.tag()) + .field("timestamp", &self.timestamp()) + .finish() + } +} diff --git a/worker-sys/src/types/websocket_request_response_pair.rs b/worker-sys/src/types/websocket_request_response_pair.rs new file mode 100644 index 00000000..07bff451 --- /dev/null +++ b/worker-sys/src/types/websocket_request_response_pair.rs @@ -0,0 +1,17 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends=js_sys::Object)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebSocketRequestResponsePair; + + #[wasm_bindgen(constructor, catch)] + pub fn new(request: &str, response: &str) -> Result; + + #[wasm_bindgen(method, getter)] + pub fn request(this: &WebSocketRequestResponsePair) -> String; + + #[wasm_bindgen(method, getter)] + pub fn response(this: &WebSocketRequestResponsePair) -> String; +} diff --git a/worker/Cargo.toml b/worker/Cargo.toml index ec94ce5d..d3efab0d 100644 --- a/worker/Cargo.toml +++ b/worker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "worker" -version = "0.3.1" +version = "0.6.7" authors = ["Cloudflare Workers Team "] repository = "https://github.com/cloudflare/workers-rs" edition = "2018" @@ -28,41 +28,21 @@ http.workspace = true http-body = "1" matchit = "0.7" pin-project = "1.1.0" -serde = { version = "1.0.164", features = ["derive"] } -serde_json = "1.0.96" +serde.workspace = true +serde_json.workspace = true tokio = { version = "1.28", default-features = false } url = "2.4.0" -serde-wasm-bindgen = "0.6.1" +serde-wasm-bindgen.workspace = true serde_urlencoded = "0.7" wasm-streams = "0.4" -worker-kv = { path = "../worker-kv", version = "0.7.0" } -worker-macros = { path = "../worker-macros", version = "0.3.1" } -worker-sys = { path = "../worker-sys", version = "0.3.1" } - -[dependencies.chrono-tz] -optional = true -version = "0.8.4" -default-features = false - -[dependencies.web-sys] -version = "0.3.63" -features = [ - "File", - "WorkerGlobalScope", - "ReadableStreamDefaultReader", - "WritableStreamDefaultWriter", -] - -[dependencies.tokio-postgres] -version = "0.7" -default-features = false -features = ["js"] -optional = true - -[dependencies.axum] -version = "0.7" -optional = true -default-features = false +worker-macros.workspace = true +worker-sys.workspace = true +chrono-tz = { version = "0.10.3", optional = true, default-features = false } +web-sys.workspace = true +tokio-postgres = { version = "0.7", optional = true, default-features = false, features = [ + "js", +] } +axum = { version = "0.8", optional = true, default-features = false } [features] queue = ["worker-macros/queue", "worker-sys/queue"] @@ -70,3 +50,6 @@ d1 = ["worker-sys/d1"] http = ["worker-macros/http"] axum = ["dep:axum"] timezone = ["dep:chrono-tz"] + +[dev-dependencies] +wasm-bindgen-test.workspace = true diff --git a/worker/src/abort.rs b/worker/src/abort.rs index ad4d69c1..220b4b8b 100644 --- a/worker/src/abort.rs +++ b/worker/src/abort.rs @@ -1,7 +1,6 @@ use std::ops::Deref; use wasm_bindgen::JsValue; -use worker_sys::ext::{AbortControllerExt, AbortSignalExt}; /// An interface that allows you to abort in-flight [Fetch](crate::Fetch) requests. #[derive(Debug)] diff --git a/worker/src/ai.rs b/worker/src/ai.rs new file mode 100644 index 00000000..da2bdbdf --- /dev/null +++ b/worker/src/ai.rs @@ -0,0 +1,84 @@ +use crate::{env::EnvBinding, send::SendFuture}; +use crate::{Error, Result}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_futures::JsFuture; +use worker_sys::Ai as AiSys; + +/// Enables access to Workers AI functionality. +#[derive(Debug)] +pub struct Ai(AiSys); + +impl Ai { + /// Execute a Workers AI operation using the specified model. + /// Various forms of the input are documented in the Workers + /// AI documentation. + pub async fn run( + &self, + model: impl AsRef, + input: T, + ) -> Result { + let fut = SendFuture::new(JsFuture::from( + self.0 + .run(model.as_ref(), serde_wasm_bindgen::to_value(&input)?), + )); + match fut.await { + Ok(output) => Ok(serde_wasm_bindgen::from_value(output)?), + Err(err) => Err(Error::from(err)), + } + } +} + +unsafe impl Sync for Ai {} +unsafe impl Send for Ai {} + +impl From for Ai { + fn from(inner: AiSys) -> Self { + Self(inner) + } +} + +impl AsRef for Ai { + fn as_ref(&self) -> &JsValue { + &self.0 + } +} + +impl From for JsValue { + fn from(database: Ai) -> Self { + JsValue::from(database.0) + } +} + +impl JsCast for Ai { + fn instanceof(val: &JsValue) -> bool { + val.is_instance_of::() + } + + fn unchecked_from_js(val: JsValue) -> Self { + Self(val.into()) + } + + fn unchecked_from_js_ref(val: &JsValue) -> &Self { + unsafe { &*(val as *const JsValue as *const Self) } + } +} + +impl EnvBinding for Ai { + const TYPE_NAME: &'static str = "Ai"; + + fn get(val: JsValue) -> Result { + let obj = js_sys::Object::from(val); + if obj.constructor().name() == Self::TYPE_NAME { + Ok(obj.unchecked_into()) + } else { + Err(format!( + "Binding cannot be cast to the type {} from {}", + Self::TYPE_NAME, + obj.constructor().name() + ) + .into()) + } + } +} diff --git a/worker/src/analytics_engine.rs b/worker/src/analytics_engine.rs new file mode 100644 index 00000000..a82c8876 --- /dev/null +++ b/worker/src/analytics_engine.rs @@ -0,0 +1,337 @@ +use crate::EnvBinding; +use crate::Result; +use js_sys::Object; +use js_sys::{Array, Uint8Array}; +use wasm_bindgen::{JsCast, JsValue}; +use worker_sys::AnalyticsEngineDataset as AnalyticsEngineSys; + +#[derive(Debug, Clone)] +pub struct AnalyticsEngineDataset(AnalyticsEngineSys); + +unsafe impl Send for AnalyticsEngineDataset {} +unsafe impl Sync for AnalyticsEngineDataset {} + +impl EnvBinding for AnalyticsEngineDataset { + const TYPE_NAME: &'static str = "AnalyticsEngineDataset"; + + // Override get to perform an unchecked cast from an object. + // Miniflare defines the binding as an Object and not a class so its name is not available for checking. + // https://github.com/cloudflare/workers-sdk/blob/main/packages/wrangler/templates/middleware/middleware-mock-analytics-engine.ts#L6 + fn get(val: JsValue) -> Result { + let obj = Object::from(val); + Ok(obj.unchecked_into()) + } +} + +impl JsCast for AnalyticsEngineDataset { + fn instanceof(val: &JsValue) -> bool { + val.is_instance_of::() + } + + fn unchecked_from_js(val: JsValue) -> Self { + Self(val.into()) + } + + fn unchecked_from_js_ref(val: &JsValue) -> &Self { + unsafe { &*(val as *const JsValue as *const Self) } + } +} + +impl From for JsValue { + fn from(analytics_engine: AnalyticsEngineDataset) -> Self { + JsValue::from(analytics_engine.0) + } +} + +impl AsRef for AnalyticsEngineDataset { + fn as_ref(&self) -> &JsValue { + &self.0 + } +} + +impl AnalyticsEngineDataset { + pub fn write_data_point(&self, event: &AnalyticsEngineDataPoint) -> Result<()> + where + AnalyticsEngineDataPoint: Clone, + JsValue: From, + { + Ok(self.0.write_data_point(event.to_js_object()?)?) + } +} + +#[derive(Debug)] +pub enum BlobType { + String(String), + Blob(Vec), +} + +impl From for JsValue { + fn from(val: BlobType) -> Self { + match val { + BlobType::String(s) => JsValue::from_str(&s), + BlobType::Blob(b) => { + let value = Uint8Array::from(b.as_slice()); + value.into() + } + } + } +} + +impl From<&str> for BlobType { + fn from(value: &str) -> Self { + BlobType::String(value.to_string()) + } +} + +impl From for BlobType { + fn from(value: String) -> Self { + BlobType::String(value) + } +} + +impl From<&[u8]> for BlobType { + fn from(value: &[u8]) -> Self { + BlobType::Blob(value.to_vec()) + } +} + +impl From> for BlobType { + fn from(value: Vec) -> Self { + BlobType::Blob(value) + } +} + +impl From<&[u8; COUNT]> for BlobType { + fn from(value: &[u8; COUNT]) -> Self { + BlobType::Blob(value.to_vec()) + } +} + +#[derive(Debug, Clone)] +pub struct AnalyticsEngineDataPoint { + indexes: Array, + doubles: Array, + blobs: Array, +} + +#[derive(Debug)] +pub struct AnalyticsEngineDataPointBuilder { + indexes: Array, + doubles: Array, + blobs: Array, +} + +impl AnalyticsEngineDataPointBuilder { + pub fn new() -> Self { + Self { + indexes: Array::new(), + doubles: Array::new(), + blobs: Array::new(), + } + } + + /// Sets the index values for the data point. + /// While the indexes field accepts an array, you currently must *only* provide a single index. + /// If you attempt to provide multiple indexes, your data point will not be recorded. + /// + /// # Arguments + /// + /// * `index`: A string or byte-array value to use as the index. + /// + /// returns: AnalyticsEngineDataPointBuilder + /// + /// # Examples + /// + /// ``` + /// use worker::AnalyticsEngineDataPointBuilder; + /// + /// let data = AnalyticsEngineDataPointBuilder::new() + /// .indexes(["index1"]) + /// .build(); + /// ``` + pub fn indexes<'index>(mut self, indexes: impl AsRef<[&'index str]>) -> Self { + let values = Array::new(); + for idx in indexes.as_ref() { + values.push(&JsValue::from_str(idx)); + } + self.indexes = values; + self + } + + /// Adds a numeric value to the end of the array of doubles. + /// + /// # Arguments + /// + /// * `double`: The numeric values that you want to record in your data point + /// + /// returns: AnalyticsEngineDataPointBuilder + /// + /// # Examples + /// + /// ``` + /// use worker::AnalyticsEngineDataPointBuilder; + /// let point = AnalyticsEngineDataPointBuilder::new() + /// .indexes(["index1"]) + /// .add_double(25) // double1 + /// .add_double(0.5) // double2 + /// .build(); + /// println!("{:?}", point); + /// ``` + pub fn add_double(self, double: impl Into) -> Self { + self.doubles.push(&JsValue::from_f64(double.into())); + self + } + + /// Set doubles1-20 with the provide values. This method will remove any doubles previously + /// added using the `add_double` method. + /// + /// # Arguments + /// + /// * `doubles`: An array of doubles + /// + /// returns: AnalyticsEngineDataPointBuilder + /// + /// # Examples + /// + /// ``` + /// use worker::AnalyticsEngineDataPointBuilder; + /// let point = AnalyticsEngineDataPointBuilder::new() + /// .indexes(["index1"]) + /// .add_double(1) // value will be replaced by the following line + /// .doubles([1, 2, 3]) // sets double1, double2 and double3 + /// .build(); + /// println!("{:?}", point); + /// ``` + pub fn doubles(mut self, doubles: impl IntoIterator) -> Self { + let values = Array::new(); + for n in doubles { + values.push(&JsValue::from_f64(n)); + } + self.doubles = values; + self + } + + /// Adds a blob-like value to the end of the array of blobs. + /// + /// # Arguments + /// + /// * `blob`: The blob values that you want to record in your data point + /// + /// returns: AnalyticsEngineDataPointBuilder + /// + /// # Examples + /// + /// ``` + /// use worker::AnalyticsEngineDataPointBuilder; + /// let point = AnalyticsEngineDataPointBuilder::new() + /// .indexes(["index1"]) + /// .add_blob("Seattle") // blob1 + /// .add_blob("USA") // blob2 + /// .add_blob("pro_sensor_9000") // blob3 + /// .build(); + /// println!("{:?}", point); + /// ``` + pub fn add_blob(self, blob: impl Into) -> Self { + let v = blob.into(); + self.blobs.push(&v.into()); + self + } + + /// Sets blobs1-20 with the provided array, replacing any values previously stored using `add_blob`. + /// + /// # Arguments + /// + /// * `blob`: The blob values that you want to record in your data point + /// + /// returns: AnalyticsEngineDataPointBuilder + /// + /// # Examples + /// + /// ``` + /// use worker::AnalyticsEngineDataPointBuilder; + /// let point = AnalyticsEngineDataPointBuilder::new() + /// .indexes(["index1"]) + /// .blobs(["Seattle", "USA", "pro_sensor_9000"]) // sets blob1, blob2, and blob3 + /// .build(); + /// println!("{:?}", point); + /// ``` + pub fn blobs(mut self, blobs: impl IntoIterator>) -> Self { + let values = Array::new(); + for blob in blobs { + let value = blob.into(); + values.push(&value.into()); + } + self.blobs = values; + self + } + + pub fn build(self) -> AnalyticsEngineDataPoint { + AnalyticsEngineDataPoint { + indexes: self.indexes, + doubles: self.doubles, + blobs: self.blobs, + } + } + + /// Write the data point to the provided analytics engine dataset. This is a convenience method + /// that can be used in place of a `.build()` followed by a call to `dataset.write_data_point(point)`. + /// + /// # Arguments + /// + /// * `dataset`: Analytics engine dataset binding + /// + /// returns: worker::Result<()> + /// + /// # Examples + /// + /// ``` + /// use worker::{Env, AnalyticsEngineDataPointBuilder, Response}; + /// use std::io::Error; + /// + /// fn main(env: Env) -> worker::Result { + /// let dataset = match env.analytics_engine("HTTP_ANALYTICS") { + /// Ok(dataset) => dataset, + /// Err(err) => return Response::error(format!("Failed to get dataset: {err:?}"), 500), + /// }; + /// + /// AnalyticsEngineDataPointBuilder::new() + /// .indexes(vec!["index1"].as_slice()) + /// .add_blob("GET") // blob1 + /// .add_double(200) // double1 + /// .write_to(&dataset)?; + /// + /// Response::ok("OK") + /// } + /// ``` + pub fn write_to(self, dataset: &AnalyticsEngineDataset) -> Result<()> { + dataset.write_data_point(&self.build()) + } +} + +// Implement From for JsValue separately for each type +impl From for JsValue { + fn from(event: AnalyticsEngineDataPoint) -> Self { + let obj = Object::new(); + + js_sys::Reflect::set(&obj, &JsValue::from_str("indexes"), &event.indexes).unwrap(); + js_sys::Reflect::set(&obj, &JsValue::from_str("doubles"), &event.doubles).unwrap(); + + let blobs = Array::new(); + for blob in event.blobs { + blobs.push(&JsValue::from(&blob)); + } + js_sys::Reflect::set(&obj, &JsValue::from_str("blobs"), &blobs).unwrap(); + + JsValue::from(obj) + } +} + +impl AnalyticsEngineDataPoint { + pub fn to_js_object(&self) -> Result + where + Self: Clone, + JsValue: From, + { + Ok(self.clone().into()) + } +} diff --git a/worker/src/cache.rs b/worker/src/cache.rs index 54d962c3..6d300fa0 100644 --- a/worker/src/cache.rs +++ b/worker/src/cache.rs @@ -103,8 +103,8 @@ impl Cache { key: K, ignore_method: bool, ) -> Result> { - let mut options = web_sys::CacheQueryOptions::new(); - options.ignore_method(ignore_method); + let options = web_sys::CacheQueryOptions::new(); + options.set_ignore_method(ignore_method); let promise = match key.into() { CacheKey::Url(url) => self @@ -136,8 +136,8 @@ impl Cache { key: K, ignore_method: bool, ) -> Result { - let mut options = web_sys::CacheQueryOptions::new(); - options.ignore_method(ignore_method); + let options = web_sys::CacheQueryOptions::new(); + options.set_ignore_method(ignore_method); let promise = match key.into() { CacheKey::Url(url) => self @@ -160,6 +160,7 @@ impl Cache { } /// The `String` or `Request` object used as the lookup key. `String`s are interpreted as the URL for a new `Request` object. +#[derive(Debug)] pub enum CacheKey<'a> { Url(String), Request(&'a Request), @@ -190,7 +191,7 @@ impl<'a> From<&'a Request> for CacheKey<'a> { } /// Successful outcomes when attempting to delete a `Response` from the cache -#[derive(Serialize)] +#[derive(Serialize, Debug, Clone)] pub enum CacheDeletionOutcome { /// The response was cached but is now deleted Success, diff --git a/worker/src/cf.rs b/worker/src/cf.rs index 39175ec8..dd29ca18 100644 --- a/worker/src/cf.rs +++ b/worker/src/cf.rs @@ -33,12 +33,12 @@ impl Cf { } /// The Autonomous System Number (ASN) of the request, e.g. `395747` - pub fn asn(&self) -> u32 { + pub fn asn(&self) -> Option { self.inner.asn().unwrap() } /// The Autonomous System organization name of the request, e.g. `Cloudflare, Inc.` - pub fn as_organization(&self) -> String { + pub fn as_organization(&self) -> Option { self.inner.as_organization().unwrap() } @@ -172,6 +172,18 @@ impl Cf { pub fn is_eu_country(&self) -> bool { self.inner.is_eu_country().unwrap() == Some("1".to_string()) } + + pub fn host_metadata(&self) -> crate::Result> { + let host_metadata = self.inner.host_metadata()?; + if host_metadata.is_undefined() { + Ok(None) + } else { + serde_wasm_bindgen::from_value(host_metadata) + .map(Some) + .map_err(|e| wasm_bindgen::JsValue::from(e.to_string())) + } + .map_err(crate::Error::from) + } } /// Browser-requested prioritization information. @@ -262,7 +274,7 @@ impl From for TlsClientAuth { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CfResponseProperties(pub(crate) js_sys::Object); impl CfResponseProperties { diff --git a/worker/src/container.rs b/worker/src/container.rs new file mode 100644 index 00000000..2378218d --- /dev/null +++ b/worker/src/container.rs @@ -0,0 +1,130 @@ +use js_sys::{Map, Object, Reflect}; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; + +use crate::{Fetcher, Result}; + +#[derive(Debug)] +pub struct Container { + pub(super) inner: worker_sys::Container, +} + +impl Container { + pub fn running(&self) -> bool { + self.inner.running() + } + + pub fn start(&self, options: Option) -> Result<()> { + let options = match options { + Some(o) => o.into(), + None => JsValue::undefined(), + }; + self.inner.start(&options).map_err(|e| e.into()) + } + + pub async fn wait_for_exit(&self) -> Result<()> { + let promise = self.inner.monitor(); + JsFuture::from(promise).await?; + Ok(()) + } + + pub async fn destroy(&self, error: Option<&str>) -> Result<()> { + let promise = self.inner.destroy(error); + JsFuture::from(promise).await?; + Ok(()) + } + + pub fn signal(&self, signo: i32) -> Result<()> { + self.inner.signal(signo).map_err(|e| e.into()) + } + + pub fn get_tcp_port(&self, port: u16) -> Result { + self.inner + .get_tcp_port(port) + .map(|f| f.into()) + .map_err(|e| e.into()) + } +} + +unsafe impl Sync for Container {} +unsafe impl Send for Container {} + +impl From for Container { + fn from(inner: worker_sys::Container) -> Self { + Self { inner } + } +} + +impl AsRef for Container { + fn as_ref(&self) -> &JsValue { + &self.inner + } +} + +impl From for JsValue { + fn from(container: Container) -> Self { + JsValue::from(container.inner) + } +} + +impl JsCast for Container { + fn instanceof(val: &JsValue) -> bool { + val.is_instance_of::() + } + + fn unchecked_from_js(val: JsValue) -> Self { + Self { inner: val.into() } + } + + fn unchecked_from_js_ref(val: &JsValue) -> &Self { + unsafe { &*(val as *const JsValue as *const Self) } + } +} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct ContainerStartupOptions { + pub entrypoint: Vec, + #[wasm_bindgen(js_name = "enableInternet")] + pub enable_internet: Option, + pub env: Map, +} + +impl ContainerStartupOptions { + pub fn new() -> ContainerStartupOptions { + ContainerStartupOptions { + entrypoint: Vec::new(), + enable_internet: None, + env: Map::new(), + } + } + + pub fn set_entrypoint(&mut self, entrypoint: &[&str]) { + self.entrypoint = entrypoint.iter().map(|s| s.to_string()).collect(); + } + + pub fn enable_internet(&mut self, enable_internet: bool) { + self.enable_internet = Some(enable_internet); + } + + pub fn add_env(&mut self, key: &str, value: &str) { + self.env + .set(&JsValue::from_str(key), &JsValue::from_str(value)); + } +} + +impl From for Object { + fn from(options: ContainerStartupOptions) -> Self { + let obj = options.clone().into(); + if !options.entrypoint.is_empty() { + Reflect::delete_property(&obj, &JsValue::from_str("entrypoint")).unwrap(); + } + if options.enable_internet.is_some() { + Reflect::delete_property(&obj, &JsValue::from_str("enableInternet")).unwrap(); + } + if options.env.size() != 0 { + Reflect::delete_property(&obj, &JsValue::from_str("env")).unwrap(); + } + obj + } +} diff --git a/worker/src/context.rs b/worker/src/context.rs index 1b59920c..f84cfe3c 100644 --- a/worker/src/context.rs +++ b/worker/src/context.rs @@ -1,7 +1,9 @@ use std::future::Future; use crate::worker_sys::Context as JsContext; +use crate::Result; +use serde::de::DeserializeOwned; use wasm_bindgen::JsValue; use wasm_bindgen_futures::future_to_promise; @@ -47,6 +49,38 @@ impl Context { pub fn pass_through_on_exception(&self) { self.inner.pass_through_on_exception().unwrap() } + + /// Get the props passed to this worker execution context. + /// + /// Props provide a way to pass additional configuration to a worker based on the context + /// in which it was invoked. For example, when your Worker is called by another Worker via + /// a Service Binding, props can provide information about the calling worker. + /// + /// Props are configured in your wrangler.toml when setting up Service Bindings: + /// ```toml + /// [[services]] + /// binding = "MY_SERVICE" + /// service = "my-worker" + /// props = { clientId = "frontend", permissions = ["read", "write"] } + /// ``` + /// + /// Then deserialize them to your custom type: + /// ```no_run + /// use serde::Deserialize; + /// + /// #[derive(Deserialize)] + /// struct MyProps { + /// clientId: String, + /// permissions: Vec, + /// } + /// + /// let props = ctx.props::()?; + /// ``` + /// + /// See: + pub fn props(&self) -> Result { + Ok(serde_wasm_bindgen::from_value(self.inner.props())?) + } } impl AsRef for Context { @@ -54,3 +88,64 @@ impl AsRef for Context { &self.inner } } + +#[cfg(test)] +mod tests { + use super::*; + use serde::Deserialize; + use wasm_bindgen::JsCast; + use wasm_bindgen_test::*; + + #[derive(Debug, Deserialize, PartialEq)] + struct TestProps { + #[serde(rename = "clientId")] + client_id: String, + permissions: Vec, + } + + #[wasm_bindgen_test] + fn test_props_deserialization() { + // Create a mock ExecutionContext with props + let obj = js_sys::Object::new(); + + // Add props to the object + let props = js_sys::Object::new(); + js_sys::Reflect::set(&props, &"clientId".into(), &"frontend-worker".into()).unwrap(); + + let permissions = js_sys::Array::new(); + permissions.push(&"read".into()); + permissions.push(&"write".into()); + js_sys::Reflect::set(&props, &"permissions".into(), &permissions.into()).unwrap(); + + js_sys::Reflect::set(&obj, &"props".into(), &props.into()).unwrap(); + + // Create a Context from the mock object + let js_context: JsContext = obj.unchecked_into(); + let context = Context::new(js_context); + + // Test that props can be deserialized + let result: Result = context.props(); + assert!(result.is_ok()); + + let props = result.unwrap(); + assert_eq!(props.client_id, "frontend-worker"); + assert_eq!(props.permissions, vec!["read", "write"]); + } + + #[wasm_bindgen_test] + fn test_props_empty_object() { + // Create a mock ExecutionContext with empty props + let obj = js_sys::Object::new(); + let props = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"props".into(), &props.into()).unwrap(); + + let js_context: JsContext = obj.unchecked_into(); + let context = Context::new(js_context); + + #[derive(Debug, Deserialize, PartialEq)] + struct EmptyProps {} + + let result: Result = context.props(); + assert!(result.is_ok()); + } +} diff --git a/worker/src/cors.rs b/worker/src/cors.rs index bb4c76af..abe6dacd 100644 --- a/worker/src/cors.rs +++ b/worker/src/cors.rs @@ -61,9 +61,9 @@ impl Cors { /// Configures which headers are allowed for cors. pub fn with_allowed_headers, V: IntoIterator>( mut self, - origins: V, + headers: V, ) -> Self { - self.allowed_headers = origins + self.allowed_headers = headers .into_iter() .map(|item| item.into()) .collect::>(); @@ -73,9 +73,9 @@ impl Cors { /// Configures which headers the client is allowed to access. pub fn with_exposed_headers, V: IntoIterator>( mut self, - origins: V, + headers: V, ) -> Self { - self.exposed_headers = origins + self.exposed_headers = headers .into_iter() .map(|item| item.into()) .collect::>(); diff --git a/worker/src/crypto.rs b/worker/src/crypto.rs index bc2a3803..aa6a96de 100644 --- a/worker/src/crypto.rs +++ b/worker/src/crypto.rs @@ -23,6 +23,7 @@ use crate::send::SendFuture; /// /// let bytes:Vec = digest_stream.digest().await.unwrap().to_vec(); /// ``` +#[derive(Debug)] pub struct DigestStream { inner: worker_sys::DigestStream, } diff --git a/worker/src/d1/mod.rs b/worker/src/d1/mod.rs index bb62b133..e2704680 100644 --- a/worker/src/d1/mod.rs +++ b/worker/src/d1/mod.rs @@ -25,6 +25,7 @@ pub use serde_wasm_bindgen; pub mod macros; // A D1 Database. +#[derive(Debug)] pub struct D1Database(D1DatabaseSys); unsafe impl Sync for D1Database {} @@ -134,6 +135,7 @@ impl From for D1Database { /// Possible argument types that can be bound to [`D1PreparedStatement`] /// See https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#type-conversion +#[derive(Debug)] pub enum D1Type<'a> { Null, Real(f64), @@ -151,6 +153,7 @@ pub enum D1Type<'a> { /// Arguments must be converted to `JsValue` when bound. If you plan to /// re-use the same argument multiple times, consider using a `D1PreparedArgument` /// which does this once on construction. +#[derive(Debug)] pub struct D1PreparedArgument<'a> { value: &'a D1Type<'a>, js_value: JsValue, @@ -207,20 +210,20 @@ pub trait D1Argument { fn js_value(&self) -> impl AsRef; } -impl<'a> D1Argument for D1Type<'a> { +impl D1Argument for D1Type<'_> { fn js_value(&self) -> impl AsRef { Into::::into(self) } } -impl<'a> D1Argument for D1PreparedArgument<'a> { +impl D1Argument for D1PreparedArgument<'_> { fn js_value(&self) -> impl AsRef { &self.js_value } } // A D1 prepared query statement. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct D1PreparedStatement(D1PreparedStatementSys); impl D1PreparedStatement { @@ -341,6 +344,7 @@ impl From for D1PreparedStatement { } // The result of a D1 query execution. +#[derive(Debug)] pub struct D1Result(D1ResultSys); // The meta object of D1 result. @@ -426,7 +430,7 @@ impl Display for D1Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let cause = self.inner.cause(); let cause = JsString::from(cause); - write!(f, "{}", cause) + write!(f, "{cause}") } } diff --git a/worker/src/delay.rs b/worker/src/delay.rs index 2d693aef..0dce1396 100644 --- a/worker/src/delay.rs +++ b/worker/src/delay.rs @@ -21,6 +21,7 @@ use wasm_bindgen::{prelude::Closure, JsCast}; /// // Waits a second /// Delay::from(duration).await; /// ``` +#[derive(Debug)] #[pin_project::pin_project(PinnedDrop)] pub struct Delay { inner: Duration, diff --git a/worker/src/durable.rs b/worker/src/durable.rs index cd2a507d..6c86456a 100644 --- a/worker/src/durable.rs +++ b/worker/src/durable.rs @@ -13,6 +13,7 @@ use std::{fmt::Display, ops::Deref, time::Duration}; use crate::{ + container::Container, date::Date, env::{Env, EnvBinding}, error::Error, @@ -21,7 +22,6 @@ use crate::{ Result, WebSocket, }; -use async_trait::async_trait; use chrono::{DateTime, Utc}; use futures_util::Future; use js_sys::{Map, Number, Object}; @@ -36,6 +36,7 @@ use worker_sys::{ use wasm_bindgen_futures::{future_to_promise, JsFuture}; /// A Durable Object stub is a client object used to send requests to a remote Durable Object. +#[derive(Debug)] pub struct Stub { inner: EdgeDurableObject, } @@ -57,12 +58,16 @@ impl Stub { let response = JsFuture::from(promise).await?; Ok(response.dyn_into::()?.into()) } + + pub fn into_rpc(self) -> T { + self.inner.unchecked_into() + } } /// Use an ObjectNamespace to get access to Stubs for communication with a Durable Object instance. /// A given namespace can support essentially unlimited Durable Objects, with each Object having /// access to a transactional, key-value storage API. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ObjectNamespace { inner: EdgeObjectNamespace, } @@ -73,7 +78,7 @@ unsafe impl Sync for ObjectNamespace {} impl ObjectNamespace { /// This method derives a unique object ID from the given name string. It will always return the /// same ID when given the same name as input. - pub fn id_from_name(&self, name: &str) -> Result { + pub fn id_from_name(&self, name: &str) -> Result> { self.inner .id_from_name(name) .map_err(Error::from) @@ -91,7 +96,7 @@ impl ObjectNamespace { /// numbers are valid IDs. This method will throw if it is passed an ID that was not originally /// created by newUniqueId() or idFromName(). It will also throw if the ID was originally /// created for a different namespace. - pub fn id_from_string(&self, hex_id: &str) -> Result { + pub fn id_from_string(&self, hex_id: &str) -> Result> { self.inner .id_from_string(hex_id) .map_err(Error::from) @@ -104,7 +109,7 @@ impl ObjectNamespace { /// Creates a new object ID randomly. This method will never return the same ID twice, and thus /// it is guaranteed that the object does not yet exist and has never existed at the time the /// method returns. - pub fn unique_id(&self) -> Result { + pub fn unique_id(&self) -> Result> { self.inner .new_unique_id() .map_err(Error::from) @@ -122,8 +127,8 @@ impl ObjectNamespace { /// currently compatible with ids created by `id_from_name()`. /// /// See supported jurisdictions and more documentation at: - /// - pub fn unique_id_with_jurisdiction(&self, jd: &str) -> Result { + /// + pub fn unique_id_with_jurisdiction(&self, jd: &str) -> Result> { let options = Object::new(); js_sys::Reflect::set(&options, &JsValue::from("jurisdiction"), &jd.into())?; self.inner @@ -134,10 +139,36 @@ impl ObjectNamespace { namespace: Some(self), }) } + + /// Get a Durable Object stub directly by name. This combines the functionality of + /// `id_from_name()` and `get_stub()` into a single method call. + pub fn get_by_name(&self, name: &str) -> Result { + self.inner + .get_by_name(name) + .map_err(Error::from) + .map(|stub| Stub { inner: stub }) + } + + /// Get a Durable Object stub directly by name with options (such as location hints). + /// This combines the functionality of `id_from_name()` and `get_stub_with_location_hint()` + /// into a single method call. + pub fn get_by_name_with_location_hint(&self, name: &str, location_hint: &str) -> Result { + let options = Object::new(); + js_sys::Reflect::set( + &options, + &JsValue::from("locationHint"), + &location_hint.into(), + )?; + self.inner + .get_by_name_with_options(name, &options) + .map_err(Error::from) + .map(|stub| Stub { inner: stub }) + } } /// An ObjectId is used to identify, locate, and access a Durable Object via interaction with its /// Stub. +#[derive(Debug)] pub struct ObjectId<'a> { inner: DurableObjectId, namespace: Option<&'a ObjectNamespace>, @@ -155,6 +186,39 @@ impl ObjectId<'_> { }) .map_err(Error::from) } + + pub fn get_stub_with_location_hint(&self, location_hint: &str) -> Result { + let options = Object::new(); + js_sys::Reflect::set( + &options, + &JsValue::from("locationHint"), + &location_hint.into(), + )?; + + self.namespace + .ok_or_else(|| JsValue::from("Cannot get stub from within a Durable Object")) + .and_then(|n| { + Ok(Stub { + inner: n.inner.get_with_options(&self.inner, &options)?, + }) + }) + .map_err(Error::from) + } + + /// The name that was used to create the `ObjectId` via [`id_from_name`](https://developers.cloudflare.com/durable-objects/api/namespace/#idfromname). + /// `None` is returned if the `ObjectId` was constructed using [`unique_id`](https://developers.cloudflare.com/durable-objects/api/namespace/#newuniqueid). + /// `None` is also returned within the Durable Object constructor, as the `name` property is not accessible there (see ). + pub fn name(&self) -> Option { + self.inner.name() + } +} + +impl PartialEq for ObjectId<'_> { + /// Compare equality between two ObjectIds using [`equals`](). + ///
The equality check ignores the namespace.
+ fn eq(&self, other: &Self) -> bool { + self.inner.equals(&other.inner) + } } impl Display for ObjectId<'_> { @@ -169,6 +233,7 @@ impl Display for ObjectId<'_> { /// Passed from the runtime to provide access to the Durable Object's storage as well as various /// metadata about the Object. +#[derive(Debug)] pub struct State { inner: DurableObjectState, } @@ -191,6 +256,10 @@ impl State { } } + pub fn container(&self) -> Option { + self.inner.container().map(|inner| Container { inner }) + } + pub fn wait_until(&self, future: F) where F: Future + 'static, @@ -203,7 +272,7 @@ impl State { .unwrap() } - // needs to be accessed by the `durable_object` macro in a conversion step + // needs to be accessed by the `#[durable_object]` macro in a conversion step pub fn _inner(self) -> DurableObjectState { self.inner } @@ -242,6 +311,14 @@ impl State { pub fn get_tags(&self, websocket: &WebSocket) -> Vec { self.inner.get_tags(websocket.as_ref()).unwrap() } + + pub fn set_websocket_auto_response(&self, pair: &worker_sys::WebSocketRequestResponsePair) { + self.inner.set_websocket_auto_response(pair).unwrap(); + } + + pub fn get_websocket_auto_response(&self) -> Option { + self.inner.get_websocket_auto_response().unwrap() + } } impl From for State { @@ -257,6 +334,12 @@ pub struct Storage { inner: DurableObjectStorage, } +impl core::fmt::Debug for Storage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Storage").finish() + } +} + impl Storage { /// Retrieves the value associated with the given key. The type of the returned value will be /// whatever was previously written for the key. @@ -287,12 +370,12 @@ impl Storage { } /// Stores the value and associates it with the given key. - pub async fn put(&mut self, key: &str, value: T) -> Result<()> { + pub async fn put(&self, key: &str, value: T) -> Result<()> { self.put_raw(key, serde_wasm_bindgen::to_value(&value)?) .await } - pub async fn put_raw(&mut self, key: &str, value: impl Into) -> Result<()> { + pub async fn put_raw(&self, key: &str, value: impl Into) -> Result<()> { JsFuture::from(self.inner.put(key, value.into())?) .await .map_err(Error::from) @@ -300,7 +383,7 @@ impl Storage { } /// Takes a serializable struct and stores each of its keys and values to storage. - pub async fn put_multiple(&mut self, values: T) -> Result<()> { + pub async fn put_multiple(&self, values: T) -> Result<()> { let values = serde_wasm_bindgen::to_value(&values)?; if !values.is_object() { return Err("Must pass in a struct type".to_string().into()); @@ -321,7 +404,7 @@ impl Storage { /// /// storage.put_multiple_raw(obj); /// ``` - pub async fn put_multiple_raw(&mut self, values: Object) -> Result<()> { + pub async fn put_multiple_raw(&self, values: Object) -> Result<()> { JsFuture::from(self.inner.put_multiple(values.into())?) .await .map_err(Error::from) @@ -329,7 +412,7 @@ impl Storage { } /// Deletes the key and associated value. Returns true if the key existed or false if it didn't. - pub async fn delete(&mut self, key: &str) -> Result { + pub async fn delete(&self, key: &str) -> Result { let fut: JsFuture = self.inner.delete(key)?.into(); fut.await .and_then(|jsv| { @@ -341,7 +424,7 @@ impl Storage { /// Deletes the provided keys and their associated values. Returns a count of the number of /// key-value pairs deleted. - pub async fn delete_multiple(&mut self, keys: Vec>) -> Result { + pub async fn delete_multiple(&self, keys: Vec>) -> Result { let fut: JsFuture = self .inner .delete_multiple( @@ -362,7 +445,7 @@ impl Storage { /// Deletes all keys and associated values, effectively deallocating all storage used by the /// Durable Object. In the event of a failure while the operation is still in flight, it may be /// that only a subset of the data is properly deleted. - pub async fn delete_all(&mut self) -> Result<()> { + pub async fn delete_all(&self) -> Result<()> { let fut: JsFuture = self.inner.delete_all()?.into(); fut.await.map(|_| ()).map_err(Error::from) } @@ -459,12 +542,12 @@ impl Storage { fut.await.map(|_| ()).map_err(Error::from) } - pub async fn transaction(&mut self, mut closure: F) -> Result<()> + pub async fn transaction(&self, closure: F) -> Result<()> where - F: FnMut(Transaction) -> Fut + Copy + 'static, + F: FnOnce(Transaction) -> Fut + 'static, Fut: Future> + 'static, { - let inner: Box js_sys::Promise> = + let inner: Box js_sys::Promise> = Box::new(move |t: DurableObjectTransaction| -> js_sys::Promise { future_to_promise(async move { closure(Transaction { inner: t }) @@ -473,14 +556,20 @@ impl Storage { .map(|_| JsValue::NULL) }) }); - let clos = wasm_bindgen::closure::Closure::wrap(inner); + let clos = wasm_bindgen::closure::Closure::once(inner); JsFuture::from(self.inner.transaction(&clos)?) .await .map_err(Error::from) .map(|_| ()) } + + // Add new method to access SQLite APIs + pub fn sql(&self) -> crate::sql::SqlStorage { + crate::sql::SqlStorage::new(self.inner.sql()) + } } +#[derive(Debug)] pub struct Transaction { inner: DurableObjectTransaction, } @@ -509,7 +598,7 @@ impl Transaction { keys.dyn_into::().map_err(Error::from) } - pub async fn put(&mut self, key: &str, value: T) -> Result<()> { + pub async fn put(&self, key: &str, value: T) -> Result<()> { JsFuture::from(self.inner.put(key, serde_wasm_bindgen::to_value(&value)?)?) .await .map_err(Error::from) @@ -517,7 +606,7 @@ impl Transaction { } // Each key-value pair in the serialized object will be added to the storage - pub async fn put_multiple(&mut self, values: T) -> Result<()> { + pub async fn put_multiple(&self, values: T) -> Result<()> { let values = serde_wasm_bindgen::to_value(&values)?; if !values.is_object() { return Err("Must pass in a struct type".to_string().into()); @@ -528,7 +617,7 @@ impl Transaction { .map(|_| ()) } - pub async fn delete(&mut self, key: &str) -> Result { + pub async fn delete(&self, key: &str) -> Result { let fut: JsFuture = self.inner.delete(key)?.into(); fut.await .and_then(|jsv| { @@ -538,7 +627,7 @@ impl Transaction { .map_err(Error::from) } - pub async fn delete_multiple(&mut self, keys: Vec>) -> Result { + pub async fn delete_multiple(&self, keys: Vec>) -> Result { let fut: JsFuture = self .inner .delete_multiple( @@ -556,7 +645,7 @@ impl Transaction { .map_err(Error::from) } - pub async fn delete_all(&mut self) -> Result<()> { + pub async fn delete_all(&self) -> Result<()> { let fut: JsFuture = self.inner.delete_all()?.into(); fut.await.map(|_| ()).map_err(Error::from) } @@ -578,12 +667,12 @@ impl Transaction { .map_err(Error::from) } - pub fn rollback(&mut self) -> Result<()> { + pub fn rollback(&self) -> Result<()> { self.inner.rollback().map_err(Error::from) } } -#[derive(Default, Serialize)] +#[derive(Default, Serialize, Debug)] pub struct ListOptions<'a> { /// Key at which the list results should start, inclusive. #[serde(skip_serializing_if = "Option::is_none")] @@ -640,7 +729,7 @@ impl<'a> ListOptions<'a> { self } } - +#[derive(Debug)] enum ScheduledTimeInit { Date(js_sys::Date), Offset(f64), @@ -655,6 +744,7 @@ enum ScheduledTimeInit { /// /// When an offset is used, the time at which `set_alarm()` or `set_alarm_with_options()` is called /// is used to compute the scheduled time. [`Date::now`] is used as the current time. +#[derive(Debug)] pub struct ScheduledTime { init: ScheduledTimeInit, } @@ -669,18 +759,18 @@ impl ScheduledTime { fn schedule(self) -> js_sys::Date { match self.init { ScheduledTimeInit::Date(date) => date, - ScheduledTimeInit::Offset(offset) => { + ScheduledTimeInit::Offset(offset_ms) => { let now = Date::now().as_millis() as f64; - js_sys::Date::new(&Number::from(now + offset)) + js_sys::Date::new(&Number::from(now + offset_ms)) } } } } impl From for ScheduledTime { - fn from(offset: i64) -> Self { + fn from(offset_ms: i64) -> Self { ScheduledTime { - init: ScheduledTimeInit::Offset(offset as f64), + init: ScheduledTimeInit::Offset(offset_ms as f64), } } } @@ -747,6 +837,7 @@ impl AsRef for ObjectNamespace { } } +#[derive(Debug)] pub enum WebSocketIncomingMessage { String(String), Binary(Vec), @@ -755,7 +846,7 @@ pub enum WebSocketIncomingMessage { /** **Note:** Implement this trait with a standard `impl DurableObject for YourType` block, but in order to integrate them with the Workers Runtime, you must also add the **`#[durable_object]`** attribute -macro to both the impl block and the struct type definition. +to the struct. ## Example ```no_run @@ -769,7 +860,6 @@ pub struct Chatroom { env: Env, // access `Env` across requests, use inside `fetch` } -#[durable_object] impl DurableObject for Chatroom { fn new(state: State, env: Env) -> Self { Self { @@ -780,47 +870,54 @@ impl DurableObject for Chatroom { } } - async fn fetch(&mut self, _req: Request) -> Result { + async fn fetch(&self, _req: Request) -> Result { // do some work when a worker makes a request to this DO Response::ok(&format!("{} active users.", self.users.len())) } } ``` */ - -#[async_trait(?Send)] -pub trait DurableObject { +#[allow(async_fn_in_trait)] // Send is not needed +pub trait DurableObject: has_durable_object_attribute { fn new(state: State, env: Env) -> Self; - async fn fetch(&mut self, req: Request) -> Result; + async fn fetch(&self, req: Request) -> Result; #[allow(clippy::diverging_sub_expression)] - async fn alarm(&mut self) -> Result { - unimplemented!("alarm() handler not implemented") + async fn alarm(&self) -> Result { + worker_sys::console_error!("alarm() handler not implemented"); + unimplemented!("alarm() handler") } #[allow(unused_variables, clippy::diverging_sub_expression)] async fn websocket_message( - &mut self, + &self, ws: WebSocket, message: WebSocketIncomingMessage, ) -> Result<()> { - unimplemented!("websocket_message() handler not implemented") + worker_sys::console_error!("websocket_message() handler not implemented"); + unimplemented!("websocket_message() handler") } #[allow(unused_variables, clippy::diverging_sub_expression)] async fn websocket_close( - &mut self, + &self, ws: WebSocket, code: usize, reason: String, was_clean: bool, ) -> Result<()> { - unimplemented!("websocket_close() handler not implemented") + worker_sys::console_error!("websocket_close() handler not implemented"); + unimplemented!("websocket_close() handler") } #[allow(unused_variables, clippy::diverging_sub_expression)] - async fn websocket_error(&mut self, ws: WebSocket, error: Error) -> Result<()> { - unimplemented!("websocket_error() handler not implemented") + async fn websocket_error(&self, ws: WebSocket, error: Error) -> Result<()> { + worker_sys::console_error!("websocket_error() handler not implemented"); + unimplemented!("websocket_error() handler") } } + +#[doc(hidden)] +#[allow(non_camel_case_types)] +pub trait has_durable_object_attribute {} diff --git a/worker/src/env.rs b/worker/src/env.rs index fe79d03a..780c2585 100644 --- a/worker/src/env.rs +++ b/worker/src/env.rs @@ -1,20 +1,24 @@ use std::fmt::Display; +use crate::analytics_engine::AnalyticsEngineDataset; #[cfg(feature = "d1")] use crate::d1::D1Database; +use crate::kv::KvStore; +use crate::rate_limit::RateLimiter; +use crate::Ai; #[cfg(feature = "queue")] use crate::Queue; -use crate::{durable::ObjectNamespace, Bucket, DynamicDispatcher, Fetcher, Result}; +use crate::{durable::ObjectNamespace, Bucket, DynamicDispatcher, Fetcher, Result, SecretStore}; use crate::{error::Error, hyperdrive::Hyperdrive}; use js_sys::Object; +use serde::de::DeserializeOwned; use wasm_bindgen::{prelude::*, JsCast, JsValue}; -use worker_kv::KvStore; #[wasm_bindgen] extern "C" { /// Env contains any bindings you have associated with the Worker when you uploaded it. - #[derive(Clone)] + #[derive(Debug, Clone)] pub type Env; } @@ -36,18 +40,38 @@ impl Env { } } + pub fn ai(&self, binding: &str) -> Result { + self.get_binding::(binding) + } + + pub fn analytics_engine(&self, binding: &str) -> Result { + self.get_binding::(binding) + } + /// Access Secret value bindings added to your Worker via the UI or `wrangler`: /// pub fn secret(&self, binding: &str) -> Result { self.get_binding::(binding) } - /// Environment variables are defined via the `[vars]` configuration in your wrangler.toml file - /// and are always plaintext values. + /// Get an environment variable defined in the [vars] section of your wrangler.toml or a secret + /// defined using `wrangler secret` as a plaintext value. + /// + /// See: pub fn var(&self, binding: &str) -> Result { self.get_binding::(binding) } + /// Get an environment variable defined in the [vars] section of your wrangler.toml that is + /// defined as an object. + /// + /// See: + pub fn object_var(&self, binding: &str) -> Result { + Ok(serde_wasm_bindgen::from_value( + self.get_binding::(binding)?.0, + )?) + } + /// Access a Workers KV namespace by the binding name configured in your wrangler.toml file. pub fn kv(&self, binding: &str) -> Result { KvStore::from_this(self, binding).map_err(From::from) @@ -86,9 +110,24 @@ impl Env { self.get_binding(binding) } + /// Access the worker assets by the binding name configured in your wrangler.toml file. + pub fn assets(&self, binding: &str) -> Result { + self.get_binding(binding) + } + pub fn hyperdrive(&self, binding: &str) -> Result { self.get_binding(binding) } + + /// Access a Secret Store by the binding name configured in your wrangler.toml file. + pub fn secret_store(&self, binding: &str) -> Result { + self.get_binding(binding) + } + + /// Access a Rate Limiter by the binding name configured in your wrangler.toml file. + pub fn rate_limiter(&self, binding: &str) -> Result { + self.get_binding(binding) + } } pub trait EnvBinding: Sized + JsCast { @@ -109,6 +148,8 @@ pub trait EnvBinding: Sized + JsCast { } } +#[repr(transparent)] +#[derive(Debug)] pub struct StringBinding(JsValue); impl EnvBinding for StringBinding { @@ -125,6 +166,7 @@ impl JsCast for StringBinding { } fn unchecked_from_js_ref(val: &JsValue) -> &Self { + // Safety: Self is marked repr(transparent) unsafe { &*(val as *const JsValue as *const Self) } } } @@ -153,6 +195,40 @@ impl Display for StringBinding { } } +#[repr(transparent)] +struct JsValueWrapper(JsValue); + +impl EnvBinding for JsValueWrapper { + const TYPE_NAME: &'static str = "Object"; +} + +impl JsCast for JsValueWrapper { + fn instanceof(_: &JsValue) -> bool { + true + } + + fn unchecked_from_js(val: JsValue) -> Self { + Self(val) + } + + fn unchecked_from_js_ref(val: &JsValue) -> &Self { + // Safety: Self is marked repr(transparent) + unsafe { &*(val as *const JsValue as *const Self) } + } +} + +impl From for wasm_bindgen::JsValue { + fn from(value: JsValueWrapper) -> Self { + value.0 + } +} + +impl AsRef for JsValueWrapper { + fn as_ref(&self) -> &JsValue { + &self.0 + } +} + /// A string value representing a binding to a secret in a Worker. #[doc(inline)] pub use StringBinding as Secret; diff --git a/worker/src/error.rs b/worker/src/error.rs index c032e2d5..d57cb72a 100644 --- a/worker/src/error.rs +++ b/worker/src/error.rs @@ -1,3 +1,4 @@ +use crate::kv::KvError; use wasm_bindgen::{JsCast, JsValue}; /// All possible Error variants that might be encountered while working with a Worker. @@ -24,6 +25,9 @@ pub enum Error { #[cfg(feature = "d1")] D1(crate::d1::D1Error), Utf8Error(std::str::Utf8Error), + #[cfg(feature = "timezone")] + TimezoneError, + KvError(KvError), } unsafe impl Sync for Error {} @@ -46,14 +50,21 @@ impl From for Error { #[cfg(feature = "http")] impl From for Error { fn from(value: http::header::InvalidHeaderName) -> Self { - Self::RustError(format!("Invalid header name: {:?}", value)) + Self::RustError(format!("Invalid header name: {value:?}")) } } #[cfg(feature = "http")] impl From for Error { fn from(value: http::header::InvalidHeaderValue) -> Self { - Self::RustError(format!("Invalid header value: {:?}", value)) + Self::RustError(format!("Invalid header value: {value:?}")) + } +} + +#[cfg(feature = "timezone")] +impl From for Error { + fn from(_value: chrono_tz::ParseError) -> Self { + Self::RustError("Invalid timezone".to_string()) } } @@ -69,10 +80,9 @@ impl From for Error { } } -impl From for Error { - fn from(e: worker_kv::KvError) -> Self { - let val: JsValue = e.into(); - val.into() +impl From for Error { + fn from(e: KvError) -> Self { + Self::KvError(e) } } @@ -126,6 +136,13 @@ impl std::fmt::Display for Error { #[cfg(feature = "d1")] Error::D1(e) => write!(f, "D1: {e:#?}"), Error::Utf8Error(e) => write!(f, "{e}"), + #[cfg(feature = "timezone")] + Error::TimezoneError => write!(f, "Timezone Error"), + Error::KvError(KvError::JavaScript(s)) => write!(f, "js error: {s:?}"), + Error::KvError(KvError::Serialization(s)) => { + write!(f, "unable to serialize/deserialize: {s}") + } + Error::KvError(KvError::InvalidKvStore(s)) => write!(f, "invalid kv store: {s}"), } } } diff --git a/worker/src/fetcher.rs b/worker/src/fetcher.rs index 0e9d7496..f7223171 100644 --- a/worker/src/fetcher.rs +++ b/worker/src/fetcher.rs @@ -1,14 +1,13 @@ use crate::{env::EnvBinding, RequestInit, Result}; -#[cfg(feature = "http")] use std::convert::TryInto; use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; #[cfg(feature = "http")] -use crate::{HttpRequest, HttpResponse}; +use crate::HttpResponse; use crate::{Request, Response}; /// A struct for invoking fetch events to other Workers. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Fetcher(worker_sys::Fetcher); #[cfg(not(feature = "http"))] @@ -16,11 +15,6 @@ type FetchResponseType = Response; #[cfg(feature = "http")] type FetchResponseType = HttpResponse; -#[cfg(not(feature = "http"))] -type FetchRequestType = Request; -#[cfg(feature = "http")] -type FetchRequestType = HttpRequest; - impl Fetcher { /// Invoke a fetch event in a worker with a url and optionally a [RequestInit]. /// @@ -47,16 +41,16 @@ impl Fetcher { /// Invoke a fetch event with an existing [Request]. /// - /// Argument type is [`Request`](crate::Request) unless `http` feature is enabled - /// and then it is [`http::Request`]. + /// Argument type is [`Request`](crate::Request) or [`http::Request`]. /// /// Return type is [`Response`](crate::Response) unless `http` feature is enabled /// and then it is [`http::Response`]. - pub async fn fetch_request(&self, request: FetchRequestType) -> Result { - #[cfg(feature = "http")] - let req = TryInto::::try_into(request)?; - #[cfg(not(feature = "http"))] - let req = request; + pub async fn fetch_request(&self, request: T) -> Result + where + T: TryInto, + crate::Error: From, + { + let req = request.try_into()?; let promise = self.0.fetch(req.inner())?; let resp_sys: web_sys::Response = JsFuture::from(promise).await?.dyn_into()?; let response = Response::from(resp_sys); diff --git a/worker/src/formdata.rs b/worker/src/formdata.rs index 499942d5..afab879f 100644 --- a/worker/src/formdata.rs +++ b/worker/src/formdata.rs @@ -12,6 +12,7 @@ use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; /// Representing the options any FormData value can be, a field or a file. +#[derive(Debug)] pub enum FormEntry { Field(String), File(File), @@ -95,18 +96,18 @@ impl FormData { /// Appends a new value onto an existing key inside a `FormData` object, or adds the key if it /// does not already exist. - pub fn append(&mut self, name: &str, value: &str) -> Result<()> { + pub fn append(&self, name: &str, value: &str) -> Result<()> { self.0.append_with_str(name, value).map_err(Error::from) } /// Sets a new value for an existing key inside a `FormData` object, or adds the key/value if it /// does not already exist. - pub fn set(&mut self, name: &str, value: &str) -> Result<()> { + pub fn set(&self, name: &str, value: &str) -> Result<()> { self.0.set_with_str(name, value).map_err(Error::from) } /// Deletes a key/value pair from a `FormData` object. - pub fn delete(&mut self, name: &str) { + pub fn delete(&self, name: &str) { self.0.delete(name) } } @@ -119,7 +120,7 @@ impl From for FormData { impl From, &dyn AsRef<&str>>> for FormData { fn from(m: HashMap<&dyn AsRef<&str>, &dyn AsRef<&str>>) -> Self { - let mut formdata = FormData::new(); + let formdata = FormData::new(); for (k, v) in m { // TODO: determine error case and consider how to handle formdata.set(k.as_ref(), v.as_ref()).unwrap(); @@ -128,8 +129,15 @@ impl From, &dyn AsRef<&str>>> for FormData { } } +impl From for wasm_bindgen::JsValue { + fn from(val: FormData) -> Self { + val.0.into() + } +} + /// A [File](https://developer.mozilla.org/en-US/docs/Web/API/File) representation used with /// `FormData`. +#[derive(Debug)] pub struct File(web_sys::File); impl File { diff --git a/worker/src/global.rs b/worker/src/global.rs index 39a5d50e..6d6462ec 100644 --- a/worker/src/global.rs +++ b/worker/src/global.rs @@ -7,6 +7,7 @@ use crate::{request::Request, response::Response, AbortSignal, Result}; /// Construct a Fetch call from a URL string or a Request object. Call its `send` method to execute /// the request. +#[derive(Debug)] pub enum Fetch { Url(url::Url), Request(Request), @@ -31,8 +32,8 @@ impl Fetch { } async fn fetch_with_str(url: &str, signal: Option<&AbortSignal>) -> Result { - let mut init = web_sys::RequestInit::new(); - init.signal(signal.map(|x| x.deref())); + let init = web_sys::RequestInit::new(); + init.set_signal(signal.map(|x| x.deref())); let worker: web_sys::WorkerGlobalScope = js_sys::global().unchecked_into(); let promise = worker.fetch_with_str_and_init(url, &init); @@ -42,8 +43,8 @@ async fn fetch_with_str(url: &str, signal: Option<&AbortSignal>) -> Result) -> Result { - let mut init = web_sys::RequestInit::new(); - init.signal(signal.map(|x| x.deref())); + let init = web_sys::RequestInit::new(); + init.set_signal(signal.map(|x| x.deref())); let worker: web_sys::WorkerGlobalScope = js_sys::global().unchecked_into(); let req = request.inner(); diff --git a/worker/src/headers.rs b/worker/src/headers.rs index dba973a0..ea7205df 100644 --- a/worker/src/headers.rs +++ b/worker/src/headers.rs @@ -44,20 +44,20 @@ impl Headers { } /// Returns an error if the name is invalid (e.g. contains spaces) - pub fn append(&mut self, name: &str, value: &str) -> Result<()> { + pub fn append(&self, name: &str, value: &str) -> Result<()> { self.0.append(name, value).map_err(Error::from) } /// Sets a new value for an existing header inside a `Headers` object, or adds the header if it does not already exist. /// Returns an error if the name is invalid (e.g. contains spaces) - pub fn set(&mut self, name: &str, value: &str) -> Result<()> { + pub fn set(&self, name: &str, value: &str) -> Result<()> { self.0.set(name, value).map_err(Error::from) } /// Deletes a header from a `Headers` object. /// Returns an error if the name is invalid (e.g. contains spaces) /// or if the JS Headers object's guard is immutable (e.g. for an incoming request) - pub fn delete(&mut self, name: &str) -> Result<()> { + pub fn delete(&self, name: &str) -> Result<()> { self.0.delete(name).map_err(Error::from) } @@ -65,8 +65,6 @@ impl Headers { pub fn entries(&self) -> HeaderIterator { self.0 .entries() - // Header.entries() doesn't error: https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries - .unwrap() .into_iter() // The entries iterator.next() will always return a proper value: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols .map((|a| a.unwrap().into()) as F1) @@ -79,8 +77,6 @@ impl Headers { pub fn keys(&self) -> impl Iterator { self.0 .keys() - // Header.keys() doesn't error: https://developer.mozilla.org/en-US/docs/Web/API/Headers/keys - .unwrap() .into_iter() // The keys iterator.next() will always return a proper value containing a string .map(|a| a.unwrap().as_string().unwrap()) @@ -91,12 +87,22 @@ impl Headers { pub fn values(&self) -> impl Iterator { self.0 .values() - // Header.values() doesn't error: https://developer.mozilla.org/en-US/docs/Web/API/Headers/values - .unwrap() .into_iter() // The values iterator.next() will always return a proper value containing a string .map(|a| a.unwrap().as_string().unwrap()) } + + /// Returns all the values of a header within a `Headers` object with a given name. + pub fn get_all(&self, name: &str) -> Result> { + let array = self.0.get_all(name); + array + .iter() + .map(|v| { + v.as_string() + .ok_or_else(|| Error::JsError("Invalid header value".into())) + }) + .collect() + } } impl Default for Headers { @@ -121,7 +127,7 @@ impl IntoIterator for &Headers { impl> FromIterator<(T, T)> for Headers { fn from_iter>(iter: U) -> Self { - let mut headers = Headers::new(); + let headers = Headers::new(); iter.into_iter().for_each(|(name, value)| { headers.append(name.as_ref(), value.as_ref()).ok(); }); @@ -131,7 +137,7 @@ impl> FromIterator<(T, T)> for Headers { impl<'a, T: AsRef> FromIterator<&'a (T, T)> for Headers { fn from_iter>(iter: U) -> Self { - let mut headers = Headers::new(); + let headers = Headers::new(); iter.into_iter().for_each(|(name, value)| { headers.append(name.as_ref(), value.as_ref()).ok(); }); diff --git a/worker/src/http.rs b/worker/src/http.rs index 9ae29480..190b6800 100644 --- a/worker/src/http.rs +++ b/worker/src/http.rs @@ -25,6 +25,7 @@ pub enum Method { Options, Connect, Trace, + Report, } impl Method { @@ -39,6 +40,7 @@ impl Method { Method::Options, Method::Connect, Method::Trace, + Method::Report, ] } } @@ -54,6 +56,7 @@ impl From for Method { "OPTIONS" => Method::Options, "CONNECT" => Method::Connect, "TRACE" => Method::Trace, + "REPORT" => Method::Report, _ => Method::Get, } } @@ -77,6 +80,7 @@ impl AsRef for Method { Method::Connect => "CONNECT", Method::Trace => "TRACE", Method::Get => "GET", + Method::Report => "REPORT", } } } @@ -84,6 +88,6 @@ impl AsRef for Method { impl Display for Method { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { let s: String = (*self).clone().into(); - write!(f, "{}", s) + write!(f, "{s}") } } diff --git a/worker/src/http/body.rs b/worker/src/http/body.rs index 523ae0b9..2c102096 100644 --- a/worker/src/http/body.rs +++ b/worker/src/http/body.rs @@ -51,7 +51,7 @@ impl Body { array.copy_from(&chunk); array.into() }) - .map_err(|err| crate::Error::RustError(format!("{:?}", err))) + .map_err(|err| crate::Error::RustError(format!("{err:?}"))) .map_err(|e| wasm_bindgen::JsValue::from(e.to_string())); let stream = wasm_streams::ReadableStream::from_stream(js_stream); diff --git a/worker/src/http/header.rs b/worker/src/http/header.rs index 09c76c2e..a47bbca9 100644 --- a/worker/src/http/header.rs +++ b/worker/src/http/header.rs @@ -1,14 +1,13 @@ use crate::Result; use http::{HeaderMap, HeaderName, HeaderValue}; use js_sys::Array; -use worker_sys::ext::HeadersExt; pub(crate) fn header_map_from_web_sys_headers( from_headers: web_sys::Headers, to_headers: &mut HeaderMap, ) -> Result<()> { // Header.entries() doesn't error: https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries - for res in from_headers.entries()?.into_iter() { + for res in from_headers.entries().into_iter() { // The entries iterator.next() will always return a proper value: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols let a: Array = res?.into(); // The entries iterator always returns an array[2] of strings diff --git a/worker/src/http/request.rs b/worker/src/http/request.rs index 9acbe44b..19936e28 100644 --- a/worker/src/http/request.rs +++ b/worker/src/http/request.rs @@ -50,17 +50,17 @@ pub fn from_wasm(req: web_sys::Request) -> Result> { pub fn to_wasm + 'static>( mut req: http::Request, ) -> Result { - let mut init = web_sys::RequestInit::new(); - init.method(req.method().as_str()); + let init = web_sys::RequestInit::new(); + init.set_method(req.method().as_str()); let headers = web_sys_headers_from_header_map(req.headers())?; - init.headers(headers.as_ref()); + init.set_headers(headers.as_ref()); let uri = req.uri().to_string(); let signal = req.extensions_mut().remove::(); - init.signal(signal.as_ref().map(|s| s.inner())); + init.set_signal(signal.as_ref().map(|s| s.inner())); if let Some(redirect) = req.extensions_mut().remove::() { - init.redirect(redirect.into()); + init.set_redirect(redirect.into()); } if let Some(cf) = req.extensions_mut().remove::() { @@ -81,7 +81,7 @@ pub fn to_wasm + 'static>( if !body.is_end_stream() { let readable_stream = wasm_streams::ReadableStream::from_stream(BodyStream::new(body)).into_raw(); - init.body(Some(readable_stream.as_ref())); + init.set_body(readable_stream.as_ref()); } Ok(web_sys::Request::new_with_str_and_init(&uri, &init)?) diff --git a/worker/src/hyperdrive.rs b/worker/src/hyperdrive.rs index 22fd26ba..04859304 100644 --- a/worker/src/hyperdrive.rs +++ b/worker/src/hyperdrive.rs @@ -3,6 +3,7 @@ use worker_sys::types::Hyperdrive as HyperdriveSys; use crate::EnvBinding; +#[derive(Debug)] pub struct Hyperdrive(HyperdriveSys); unsafe impl Send for Hyperdrive {} diff --git a/worker/src/kv/builder.rs b/worker/src/kv/builder.rs new file mode 100644 index 00000000..4a2beae9 --- /dev/null +++ b/worker/src/kv/builder.rs @@ -0,0 +1,305 @@ +use js_sys::{ArrayBuffer, Function, Object, Promise, Uint8Array}; +use serde::{de::DeserializeOwned, Serialize}; +use serde_json::Value; +use serde_wasm_bindgen::Serializer; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_futures::JsFuture; + +use crate::kv::{KvError, ListResponse}; + +/// A builder to configure put requests. +#[derive(Debug, Clone)] +#[must_use = "PutOptionsBuilder does nothing until you 'execute' it"] +pub struct PutOptionsBuilder { + pub(crate) this: Object, + pub(crate) put_function: Function, + pub(crate) name: JsValue, + pub(crate) value: JsValue, + pub(crate) expiration: Option, + pub(crate) expiration_ttl: Option, + pub(crate) metadata: Option, +} + +#[derive(Serialize)] +struct PutOptions { + #[serde(skip_serializing_if = "Option::is_none")] + expiration: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "expirationTtl")] + expiration_ttl: Option, + #[serde(skip_serializing_if = "Option::is_none")] + metadata: Option, +} + +impl PutOptionsBuilder { + /// When (expressed as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time)) the key + /// value pair will expire in the store. + pub fn expiration(mut self, expiration: u64) -> Self { + self.expiration = Some(expiration); + self + } + /// How many seconds until the key value pair will expire. + pub fn expiration_ttl(mut self, expiration_ttl: u64) -> Self { + self.expiration_ttl = Some(expiration_ttl); + self + } + /// Metadata to be stored with the key value pair. + pub fn metadata(mut self, metadata: T) -> Result { + self.metadata = Some(serde_json::to_value(metadata)?); + Ok(self) + } + /// Puts the value in the kv store. + pub async fn execute(self) -> Result<(), KvError> { + let ser = Serializer::json_compatible(); + let options_object = PutOptions { + expiration: self.expiration, + expiration_ttl: self.expiration_ttl, + metadata: self.metadata, + } + .serialize(&ser) + .map_err(JsValue::from)?; + + let promise: Promise = self + .put_function + .call3(&self.this, &self.name, &self.value, &options_object)? + .into(); + JsFuture::from(promise) + .await + .map(|_| ()) + .map_err(KvError::from) + } +} + +/// A builder to configure list requests. +#[derive(Debug, Clone)] +#[must_use = "ListOptionsBuilder does nothing until you 'execute' it"] +pub struct ListOptionsBuilder { + pub(crate) this: Object, + pub(crate) list_function: Function, + pub(crate) limit: Option, + pub(crate) cursor: Option, + pub(crate) prefix: Option, +} + +#[derive(Serialize)] +struct ListOptions { + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) cursor: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) prefix: Option, +} + +impl ListOptionsBuilder { + /// The maximum number of keys returned. The default is 1000, which is the maximum. It is + /// unlikely that you will want to change this default, but it is included for completeness. + pub fn limit(mut self, limit: u64) -> Self { + self.limit = Some(limit); + self + } + /// A string returned by a previous response used to paginate the keys in the store. + pub fn cursor(mut self, cursor: String) -> Self { + self.cursor = Some(cursor); + self + } + /// A prefix that all keys must start with for them to be included in the response. + pub fn prefix(mut self, prefix: String) -> Self { + self.prefix = Some(prefix); + self + } + /// Lists the key value pairs in the kv store. + pub async fn execute(self) -> Result { + let ser = Serializer::json_compatible(); + let options_object = ListOptions { + limit: self.limit, + cursor: self.cursor, + prefix: self.prefix, + } + .serialize(&ser) + .map_err(JsValue::from)?; + + let promise: Promise = self + .list_function + .call1(&self.this, &options_object)? + .into(); + + let value = JsFuture::from(promise).await?; + let resp = serde_wasm_bindgen::from_value(value).map_err(JsValue::from)?; + Ok(resp) + } +} + +/// A builder to configure get requests. +#[derive(Debug, Clone)] +#[must_use = "GetOptionsBuilder does nothing until you 'get' it"] +pub struct GetOptionsBuilder { + pub(crate) this: Object, + pub(crate) get_function: Function, + pub(crate) get_with_meta_function: Function, + pub(crate) name: JsValue, + pub(crate) cache_ttl: Option, + pub(crate) value_type: Option, +} + +#[derive(Serialize)] +struct GetOptions { + #[serde(rename = "cacheTtl", skip_serializing_if = "Option::is_none")] + pub(crate) cache_ttl: Option, + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub(crate) value_type: Option, +} + +impl GetOptionsBuilder { + /// The cache_ttl parameter must be an integer that is greater than or equal to 60. It defines + /// the length of time in seconds that a KV result is cached in the edge location that it is + /// accessed from. This can be useful for reducing cold read latency on keys that are read + /// relatively infrequently. It is especially useful if your data is write-once or + /// write-rarely, but is not recommended if your data is updated often and you need to see + /// updates shortly after they're written, because writes that happen from other edge locations + /// won't be visible until the cached value expires. + pub fn cache_ttl(mut self, cache_ttl: u64) -> Self { + self.cache_ttl = Some(cache_ttl); + self + } + + fn value_type(mut self, value_type: GetValueType) -> Self { + self.value_type = Some(value_type); + self + } + + fn options(&self) -> Result { + let ser = Serializer::json_compatible(); + Ok(GetOptions { + cache_ttl: self.cache_ttl, + value_type: self.value_type, + } + .serialize(&ser) + .map_err(JsValue::from)?) + } + + async fn get(self) -> Result { + let options_object = self.options()?; + + let promise: Promise = self + .get_function + .call2(&self.this, &self.name, &options_object)? + .into(); + JsFuture::from(promise).await.map_err(KvError::from) + } + + /// Gets the value as a string. + pub async fn text(self) -> Result, KvError> { + let value = self.value_type(GetValueType::Text).get().await?; + Ok(value.as_string()) + } + + /// Tries to deserialize the inner text to the generic type. + pub async fn json(self) -> Result, KvError> + where + T: DeserializeOwned, + { + let value = self.value_type(GetValueType::Json).get().await?; + Ok(if value.is_null() { + None + } else { + Some(serde_wasm_bindgen::from_value(value).map_err(JsValue::from)?) + }) + } + + /// Gets the value as a byte slice. + pub async fn bytes(self) -> Result>, KvError> { + let v = self.value_type(GetValueType::ArrayBuffer).get().await?; + if ArrayBuffer::instanceof(&v) { + let buffer = ArrayBuffer::from(v); + let buffer = Uint8Array::new(&buffer); + Ok(Some(buffer.to_vec())) + } else { + Ok(None) + } + } + + async fn get_with_metadata(&self) -> Result<(JsValue, Option), KvError> + where + M: DeserializeOwned, + { + let options_object = self.options()?; + + let promise: Promise = self + .get_with_meta_function + .call2(&self.this, &self.name, &options_object)? + .into(); + + let pair = JsFuture::from(promise).await?; + let metadata = crate::kv::get(&pair, "metadata")?; + let value = crate::kv::get(&pair, "value")?; + + Ok(( + value, + if metadata.is_null() { + None + } else { + Some(serde_wasm_bindgen::from_value(metadata).map_err(JsValue::from)?) + }, + )) + } + + /// Gets the value as a string and it's associated metadata. + pub async fn text_with_metadata(self) -> Result<(Option, Option), KvError> + where + M: DeserializeOwned, + { + let (value, metadata) = self + .value_type(GetValueType::Text) + .get_with_metadata() + .await?; + Ok((value.as_string(), metadata)) + } + + /// Tries to deserialize the inner text to the generic type along with it's metadata. + pub async fn json_with_metadata(self) -> Result<(Option, Option), KvError> + where + T: DeserializeOwned, + M: DeserializeOwned, + { + let (value, metadata) = self + .value_type(GetValueType::Json) + .get_with_metadata() + .await?; + Ok(( + if value.is_null() { + None + } else { + Some(serde_wasm_bindgen::from_value(value).map_err(JsValue::from)?) + }, + metadata, + )) + } + + /// Gets the value as a byte slice and it's associated metadata. + pub async fn bytes_with_metadata(self) -> Result<(Option>, Option), KvError> + where + M: DeserializeOwned, + { + let (value, metadata) = self + .value_type(GetValueType::ArrayBuffer) + .get_with_metadata() + .await?; + + if ArrayBuffer::instanceof(&value) { + let buffer = ArrayBuffer::from(value); + let buffer = Uint8Array::new(&buffer); + Ok((Some(buffer.to_vec()), metadata)) + } else { + Ok((None, metadata)) + } + } +} + +#[derive(Debug, Clone, Serialize, Copy)] +#[serde(rename_all = "camelCase")] +pub(crate) enum GetValueType { + Text, + ArrayBuffer, + Json, +} diff --git a/worker/src/kv/mod.rs b/worker/src/kv/mod.rs new file mode 100644 index 00000000..209d9b7d --- /dev/null +++ b/worker/src/kv/mod.rs @@ -0,0 +1,231 @@ +//! Bindings to Cloudflare Worker's [KV](https://developers.cloudflare.com/workers/runtime-apis/kv) +//! to be used ***inside*** of a worker's context. +//! +//! # Example +//! ```ignore +//! let kv = KvStore::create("Example")?; +//! +//! // Insert a new entry into the kv. +//! kv.put("example_key", "example_value")? +//! .metadata(vec![1, 2, 3, 4]) // Use some arbitrary serialiazable metadata +//! .execute() +//! .await?; +//! +//! // NOTE: kv changes can take a minute to become visible to other workers. +//! // Get that same metadata. +//! let (value, metadata) = kv.get("example_key").text_with_metadata::>().await?; +//! ``` +#[forbid(missing_docs)] +mod builder; + +pub use builder::*; + +use js_sys::{global, Function, Object, Promise, Reflect, Uint8Array}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; + +/// A binding to a Cloudflare KvStore. +#[derive(Clone, Debug)] +pub struct KvStore { + pub(crate) this: Object, + pub(crate) get_function: Function, + pub(crate) get_with_meta_function: Function, + pub(crate) put_function: Function, + pub(crate) list_function: Function, + pub(crate) delete_function: Function, +} + +// Allows for attachment to axum router, as Workers will never allow multithreading. +unsafe impl Send for KvStore {} +unsafe impl Sync for KvStore {} + +impl KvStore { + /// Creates a new [`KvStore`] with the binding specified in your `wrangler.toml`. + pub fn create(binding: &str) -> Result { + let this = get(&global(), binding)?; + + // Ensures that the kv store exists. + if this.is_undefined() { + Err(KvError::InvalidKvStore(binding.into())) + } else { + Ok(Self { + get_function: get(&this, "get")?.into(), + get_with_meta_function: get(&this, "getWithMetadata")?.into(), + put_function: get(&this, "put")?.into(), + list_function: get(&this, "list")?.into(), + delete_function: get(&this, "delete")?.into(), + this: this.into(), + }) + } + } + + /// Creates a new [`KvStore`] with the binding specified in your `wrangler.toml`, using an + /// alternative `this` value for arbitrary binding contexts. + pub fn from_this(this: &JsValue, binding: &str) -> Result { + let this = get(this, binding)?; + + // Ensures that the kv store exists. + if this.is_undefined() { + Err(KvError::InvalidKvStore(binding.into())) + } else { + Ok(Self { + get_function: get(&this, "get")?.into(), + get_with_meta_function: get(&this, "getWithMetadata")?.into(), + put_function: get(&this, "put")?.into(), + list_function: get(&this, "list")?.into(), + delete_function: get(&this, "delete")?.into(), + this: this.into(), + }) + } + } + + /// Fetches the value from the kv store by name. + pub fn get(&self, name: &str) -> GetOptionsBuilder { + GetOptionsBuilder { + this: self.this.clone(), + get_function: self.get_function.clone(), + get_with_meta_function: self.get_with_meta_function.clone(), + name: JsValue::from(name), + cache_ttl: None, + value_type: None, + } + } + + /// Puts data into the kv store. + pub fn put(&self, name: &str, value: T) -> Result { + Ok(PutOptionsBuilder { + this: self.this.clone(), + put_function: self.put_function.clone(), + name: JsValue::from(name), + value: value.raw_kv_value()?, + expiration: None, + expiration_ttl: None, + metadata: None, + }) + } + + /// Puts the specified byte slice into the kv store. + pub fn put_bytes(&self, name: &str, value: &[u8]) -> Result { + let typed_array = Uint8Array::new_with_length(value.len() as u32); + typed_array.copy_from(value); + let value: JsValue = typed_array.buffer().into(); + Ok(PutOptionsBuilder { + this: self.this.clone(), + put_function: self.put_function.clone(), + name: JsValue::from(name), + value, + expiration: None, + expiration_ttl: None, + metadata: None, + }) + } + + /// Lists the keys in the kv store. + pub fn list(&self) -> ListOptionsBuilder { + ListOptionsBuilder { + this: self.this.clone(), + list_function: self.list_function.clone(), + limit: None, + cursor: None, + prefix: None, + } + } + + /// Deletes a key in the kv store. + pub async fn delete(&self, name: &str) -> Result<(), KvError> { + let name = JsValue::from(name); + let promise: Promise = self.delete_function.call1(&self.this, &name)?.into(); + JsFuture::from(promise).await?; + Ok(()) + } +} + +/// The response for listing the elements in a KV store. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListResponse { + /// A slice of all of the keys in the KV store. + pub keys: Vec, + /// If there are more keys that can be fetched using the response's cursor. + pub list_complete: bool, + /// A string used for paginating responses. + pub cursor: Option, +} + +/// The representation of a key in the KV store. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Key { + /// The name of the key. + pub name: String, + /// When (expressed as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time)) the key + /// value pair will expire in the store. + pub expiration: Option, + /// All metadata associated with the key. + pub metadata: Option, +} + +/// A simple error type that can occur during kv operations. +#[derive(Debug)] +pub enum KvError { + JavaScript(JsValue), + Serialization(serde_json::Error), + InvalidKvStore(String), +} + +impl From for JsValue { + fn from(val: KvError) -> Self { + match val { + KvError::JavaScript(value) => value, + KvError::Serialization(e) => format!("KvError::Serialization: {e}").into(), + KvError::InvalidKvStore(binding) => { + format!("KvError::InvalidKvStore: {binding}").into() + } + } + } +} + +impl From for KvError { + fn from(value: JsValue) -> Self { + Self::JavaScript(value) + } +} + +impl From for KvError { + fn from(value: serde_json::Error) -> Self { + Self::Serialization(value) + } +} + +/// A trait for things that can be converted to [`wasm_bindgen::JsValue`] to be passed to the kv. +pub trait ToRawKvValue { + fn raw_kv_value(&self) -> Result; +} + +impl ToRawKvValue for str { + fn raw_kv_value(&self) -> Result { + Ok(JsValue::from(self)) + } +} + +impl ToRawKvValue for T { + fn raw_kv_value(&self) -> Result { + let value = serde_wasm_bindgen::to_value(self).map_err(JsValue::from)?; + + if value.as_string().is_some() { + Ok(value) + } else if let Some(number) = value.as_f64() { + Ok(JsValue::from(number.to_string())) + } else if let Some(boolean) = value.as_bool() { + Ok(JsValue::from(boolean.to_string())) + } else { + js_sys::JSON::stringify(&value) + .map(JsValue::from) + .map_err(Into::into) + } + } +} + +fn get(target: &JsValue, name: &str) -> Result { + Reflect::get(target, &JsValue::from(name)) +} diff --git a/worker/src/lib.rs b/worker/src/lib.rs index 91d8e042..ccc1a919 100644 --- a/worker/src/lib.rs +++ b/worker/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::new_without_default)] #![allow(clippy::or_fun_call)] +#![warn(missing_debug_implementations)] //! # Features //! ## `d1` //! @@ -147,23 +148,23 @@ use std::result::Result as StdResult; #[doc(hidden)] pub use async_trait; -#[doc(hidden)] pub use js_sys; pub use url::Url; -#[doc(hidden)] pub use wasm_bindgen; -#[doc(hidden)] pub use wasm_bindgen_futures; -pub use worker_kv as kv; +pub use web_sys; pub use cf::{Cf, CfResponseProperties, TlsClientAuth}; -pub use worker_macros::{durable_object, event, send}; +pub use worker_macros::{consume, durable_object, event, send}; #[doc(hidden)] pub use worker_sys; pub use worker_sys::{console_debug, console_error, console_log, console_warn}; pub use crate::abort::*; +pub use crate::ai::*; +pub use crate::analytics_engine::*; pub use crate::cache::{Cache, CacheDeletionOutcome, CacheKey}; +pub use crate::container::*; pub use crate::context::Context; pub use crate::cors::Cors; #[cfg(feature = "d1")] @@ -180,24 +181,32 @@ pub use crate::global::Fetch; pub use crate::headers::Headers; pub use crate::http::Method; pub use crate::hyperdrive::*; +pub use crate::kv::{KvError, KvStore}; #[cfg(feature = "queue")] pub use crate::queue::*; pub use crate::r2::*; +pub use crate::rate_limit::{RateLimitOutcome, RateLimiter}; pub use crate::request::{FromRequest, Request}; pub use crate::request_init::*; pub use crate::response::{EncodeBody, IntoResponse, Response, ResponseBody, ResponseBuilder}; pub use crate::router::{RouteContext, RouteParams, Router}; pub use crate::schedule::*; +pub use crate::secret_store::SecretStore; pub use crate::socket::*; pub use crate::streams::*; +pub use crate::version::*; pub use crate::websocket::*; mod abort; +mod ai; +mod analytics_engine; mod cache; mod cf; +mod container; mod context; mod cors; pub mod crypto; +pub mod panic_abort; // Require pub module for macro export #[cfg(feature = "d1")] /// **Requires** `d1` feature. @@ -214,16 +223,20 @@ mod global; mod headers; mod http; mod hyperdrive; +pub mod kv; #[cfg(feature = "queue")] mod queue; mod r2; +mod rate_limit; mod request; mod request_init; mod response; mod router; mod schedule; +mod secret_store; pub mod send; mod socket; +mod sql; mod streams; mod version; mod websocket; @@ -245,3 +258,5 @@ pub type HttpRequest = ::http::Request; #[cfg(feature = "http")] /// **Requires** `http` feature. Type alias for `http::Response`. pub type HttpResponse = ::http::Response; + +pub use crate::sql::*; diff --git a/worker/src/panic_abort.rs b/worker/src/panic_abort.rs new file mode 100644 index 00000000..17527df5 --- /dev/null +++ b/worker/src/panic_abort.rs @@ -0,0 +1,64 @@ +use std::panic; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +#[cfg(target_arch = "wasm32")] +use std::cell::Cell; + +#[cfg(target_arch = "wasm32")] +thread_local! { + static PANIC_CALLBACK: Cell> = Cell::new(None); +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen(js_name = "setPanicHook")] +pub fn set_panic_hook(callback: js_sys::Function) { + PANIC_CALLBACK.with(|f| f.set(Some(callback))); + set_once(); +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn set_panic_hook(_callback: ()) { + // No-op on non-wasm targets +} + +#[allow(deprecated)] +#[cfg(target_arch = "wasm32")] +fn hook_impl(info: &panic::PanicInfo) { + let message = info.to_string(); + + PANIC_CALLBACK.with(|f| { + if let Some(callback) = f.take() { + use js_sys::JsString; + + if let Err(e) = callback.call1(&JsValue::UNDEFINED, &JsString::from(message)) { + web_sys::console::error_2(&"Failed to call panic callback:".into(), &e); + } + } + }); +} + +#[allow(deprecated)] +#[cfg(not(target_arch = "wasm32"))] +fn hook_impl(_info: &panic::PanicInfo) { + // On non-wasm targets, we don't have contexts to abort + // This is a no-op, but maintains the same interface +} + +/// Set the WASM reinitialization panic hook the first time this is called. +/// Subsequent invocations do nothing. +#[allow(dead_code)] +#[inline] +fn set_once() { + use std::sync::Once; + static SET_HOOK: Once = Once::new(); + SET_HOOK.call_once(|| { + let default_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + // First call the existing hook (console_error_panic_hook if set) + default_hook(panic_info); + hook_impl(panic_info); + })); + }); +} diff --git a/worker/src/queue.rs b/worker/src/queue.rs index 7d104ab9..75529cdf 100644 --- a/worker/src/queue.rs +++ b/worker/src/queue.rs @@ -11,6 +11,7 @@ use wasm_bindgen_futures::JsFuture; use worker_sys::{Message as MessageSys, MessageBatch as MessageBatchSys, Queue as EdgeQueue}; /// A batch of messages that are sent to a consumer Worker. +#[derive(Debug)] pub struct MessageBatch { inner: MessageBatchSys, phantom: PhantomData, @@ -77,6 +78,7 @@ impl From for MessageBatch { } /// A message that is sent to a consumer Worker. +#[derive(Debug)] pub struct Message { inner: MessageSys, body: T, @@ -115,6 +117,7 @@ where } /// A message that is sent to a consumer Worker. +#[derive(Debug)] pub struct RawMessage { inner: MessageSys, } @@ -150,11 +153,13 @@ impl MessageSysInner for Message { #[derive(Serialize)] #[serde(rename_all = "camelCase")] +#[derive(Debug)] /// Optional configuration when marking a message or a batch of messages for retry. pub struct QueueRetryOptions { delay_seconds: Option, } +#[derive(Debug)] pub struct QueueRetryOptionsBuilder { delay_seconds: Option, } @@ -229,6 +234,7 @@ impl MessageExt for T { } } +#[derive(Debug)] pub struct MessageIter { range: std::ops::Range, array: Array, @@ -270,6 +276,7 @@ impl std::iter::FusedIterator for MessageIter where T: DeserializeOwned {} impl std::iter::ExactSizeIterator for MessageIter where T: DeserializeOwned {} +#[derive(Debug)] pub struct RawMessageIter { range: std::ops::Range, array: Array, @@ -302,7 +309,7 @@ impl std::iter::FusedIterator for RawMessageIter {} impl std::iter::ExactSizeIterator for RawMessageIter {} -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Queue(EdgeQueue); unsafe impl Send for Queue {} @@ -361,13 +368,14 @@ impl Serialize for QueueContentType { } } -#[derive(Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct QueueSendOptions { content_type: Option, delay_seconds: Option, } +#[derive(Debug)] pub struct MessageBuilder { message: T, delay_seconds: Option, @@ -411,6 +419,7 @@ impl MessageBuilder { } } +#[derive(Debug)] pub struct RawMessageBuilder { message: JsValue, delay_seconds: Option, @@ -449,6 +458,7 @@ impl RawMessageBuilder { /// This type can't be constructed directly. /// /// It should be constructed using the `MessageBuilder`, `RawMessageBuilder` or by calling `.into()` on a struct that is `serializable`. +#[derive(Debug)] pub struct SendMessage { /// The body of the message. /// @@ -480,17 +490,19 @@ impl From for SendMessage { } } +#[derive(Debug)] pub struct BatchSendMessage { body: Vec>, options: Option, } -#[derive(Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct QueueSendBatchOptions { delay_seconds: Option, } +#[derive(Debug)] pub struct BatchMessageBuilder { messages: Vec>, delay_seconds: Option, diff --git a/worker/src/r2/builder.rs b/worker/src/r2/builder.rs index df8bce32..e339695f 100644 --- a/worker/src/r2/builder.rs +++ b/worker/src/r2/builder.rs @@ -13,6 +13,7 @@ use crate::{Date, Error, MultipartUpload, ObjectInner, Objects, Result}; use super::{Data, Object}; /// Options for configuring the [get](crate::r2::Bucket::get) operation. +#[derive(Debug)] pub struct GetOptionsBuilder<'bucket> { pub(crate) edge_bucket: &'bucket EdgeR2Bucket, pub(crate) key: String, @@ -20,7 +21,7 @@ pub struct GetOptionsBuilder<'bucket> { pub(crate) range: Option, } -impl<'bucket> GetOptionsBuilder<'bucket> { +impl GetOptionsBuilder<'_> { /// Specifies that the object should only be returned given satisfaction of certain conditions /// in the [R2Conditional]. Refer to [Conditional operations](https://developers.cloudflare.com/r2/runtime-apis/#conditional-operations). pub fn only_if(mut self, only_if: Conditional) -> Self { @@ -164,6 +165,7 @@ impl TryFrom for Range { } /// Options for configuring the [put](crate::r2::Bucket::put) operation. +#[derive(Debug)] pub struct PutOptionsBuilder<'bucket> { pub(crate) edge_bucket: &'bucket EdgeR2Bucket, pub(crate) key: String, @@ -174,7 +176,7 @@ pub struct PutOptionsBuilder<'bucket> { pub(crate) checksum_algorithm: String, } -impl<'bucket> PutOptionsBuilder<'bucket> { +impl PutOptionsBuilder<'_> { /// Various HTTP headers associated with the object. Refer to [HttpMetadata]. pub fn http_metadata(mut self, metadata: HttpMetadata) -> Self { self.http_metadata = Some(metadata); @@ -258,6 +260,7 @@ impl<'bucket> PutOptionsBuilder<'bucket> { } /// Options for configuring the [create_multipart_upload](crate::r2::Bucket::create_multipart_upload) operation. +#[derive(Debug)] pub struct CreateMultipartUploadOptionsBuilder<'bucket> { pub(crate) edge_bucket: &'bucket EdgeR2Bucket, pub(crate) key: String, @@ -265,7 +268,7 @@ pub struct CreateMultipartUploadOptionsBuilder<'bucket> { pub(crate) custom_metadata: Option>, } -impl<'bucket> CreateMultipartUploadOptionsBuilder<'bucket> { +impl CreateMultipartUploadOptionsBuilder<'_> { /// Various HTTP headers associated with the object. Refer to [HttpMetadata]. pub fn http_metadata(mut self, metadata: HttpMetadata) -> Self { self.http_metadata = Some(metadata); @@ -353,6 +356,7 @@ impl From for HttpMetadata { } /// Options for configuring the [list](crate::r2::Bucket::list) operation. +#[derive(Debug)] pub struct ListOptionsBuilder<'bucket> { pub(crate) edge_bucket: &'bucket EdgeR2Bucket, pub(crate) limit: Option, @@ -362,7 +366,7 @@ pub struct ListOptionsBuilder<'bucket> { pub(crate) include: Option>, } -impl<'bucket> ListOptionsBuilder<'bucket> { +impl ListOptionsBuilder<'_> { /// The number of results to return. Defaults to 1000, with a maximum of 1000. pub fn limit(mut self, limit: u32) -> Self { self.limit = Some(limit); diff --git a/worker/src/r2/mod.rs b/worker/src/r2/mod.rs index 1f2bcaec..8b1dab9e 100644 --- a/worker/src/r2/mod.rs +++ b/worker/src/r2/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, convert::TryInto}; +use std::{collections::HashMap, convert::TryInto, ops::Deref}; pub use builder::*; @@ -19,7 +19,7 @@ use crate::{ mod builder; /// An instance of the R2 bucket binding. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Bucket { inner: EdgeR2Bucket, } @@ -42,7 +42,7 @@ impl Bucket { /// Retrieves the [Object] for the given key containing object metadata and the object body if /// the key exists. In the event that a precondition specified in options fails, get() returns /// an [Object] with no body. - pub fn get(&self, key: impl Into) -> GetOptionsBuilder { + pub fn get(&self, key: impl Into) -> GetOptionsBuilder<'_> { GetOptionsBuilder { edge_bucket: &self.inner, key: key.into(), @@ -56,7 +56,7 @@ impl Bucket { /// /// R2 writes are strongly consistent. Once the future resolves, all subsequent read operations /// will see this key value pair globally. - pub fn put(&self, key: impl Into, value: impl Into) -> PutOptionsBuilder { + pub fn put(&self, key: impl Into, value: impl Into) -> PutOptionsBuilder<'_> { PutOptionsBuilder { edge_bucket: &self.inner, key: key.into(), @@ -79,9 +79,26 @@ impl Bucket { Ok(()) } + /// Deletes the given values and metadata under the associated keys. Once + /// the delete succeeds, returns void. + /// + /// R2 deletes are strongly consistent. Once the Promise resolves, all + /// subsequent read operations will no longer see the provided key value + /// pairs globally. + /// + /// Up to 1000 keys may be deleted per call. + pub async fn delete_multiple(&self, keys: Vec>) -> Result<()> { + let fut: JsFuture = self + .inner + .delete_multiple(keys.into_iter().map(|key| JsValue::from(&*key)).collect())? + .into(); + fut.await?; + Ok(()) + } + /// Returns an [Objects] containing a list of [Objects]s contained within the bucket. By /// default, returns the first 1000 entries. - pub fn list(&self) -> ListOptionsBuilder { + pub fn list(&self) -> ListOptionsBuilder<'_> { ListOptionsBuilder { edge_bucket: &self.inner, limit: None, @@ -100,7 +117,7 @@ impl Bucket { pub fn create_multipart_upload( &self, key: impl Into, - ) -> CreateMultipartUploadOptionsBuilder { + ) -> CreateMultipartUploadOptionsBuilder<'_> { CreateMultipartUploadOptionsBuilder { edge_bucket: &self.inner, key: key.into(), @@ -161,6 +178,7 @@ impl AsRef for Bucket { /// [Object] is created when you [put](Bucket::put) an object into a [Bucket]. [Object] represents /// the metadata of an object based on the information provided by the uploader. Every object that /// you [put](Bucket::put) into a [Bucket] will have an [Object] created. +#[derive(Debug)] pub struct Object { inner: ObjectInner, } @@ -180,11 +198,12 @@ impl Object { } } - pub fn size(&self) -> u32 { - match &self.inner { + pub fn size(&self) -> u64 { + let size = match &self.inner { ObjectInner::NoBody(inner) => inner.size().unwrap(), ObjectInner::Body(inner) => inner.size().unwrap(), - } + }; + size.round() as u64 } pub fn etag(&self) -> String { @@ -251,7 +270,7 @@ impl Object { .try_into() } - pub fn body(&self) -> Option { + pub fn body(&self) -> Option> { match &self.inner { ObjectInner::NoBody(_) => None, ObjectInner::Body(body) => Some(ObjectBody { inner: body }), @@ -276,11 +295,12 @@ impl Object { } /// The data contained within an [Object]. +#[derive(Debug)] pub struct ObjectBody<'body> { inner: &'body EdgeR2ObjectBody, } -impl<'body> ObjectBody<'body> { +impl ObjectBody<'_> { /// Reads the data in the [Object] via a [ByteStream]. pub fn stream(self) -> Result { if self.inner.body_used()? { @@ -324,6 +344,7 @@ impl<'body> ObjectBody<'body> { /// [UploadedPart] represents a part that has been uploaded. /// [UploadedPart] objects are returned from [upload_part](MultipartUpload::upload_part) operations /// and must be passed to the [complete](MultipartUpload::complete) operation. +#[derive(Debug)] pub struct UploadedPart { inner: EdgeR2UploadedPart, } @@ -352,6 +373,7 @@ impl UploadedPart { } } +#[derive(Debug)] pub struct MultipartUpload { inner: EdgeR2MultipartUpload, } @@ -414,6 +436,7 @@ impl MultipartUpload { } /// A series of [Object]s returned by [list](Bucket::list). +#[derive(Debug)] pub struct Objects { inner: EdgeR2Objects, } @@ -459,12 +482,13 @@ impl Objects { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) enum ObjectInner { NoBody(EdgeR2Object), Body(EdgeR2ObjectBody), } +#[derive(Debug)] pub enum Data { ReadableStream(web_sys::ReadableStream), Stream(FixedLengthStream), diff --git a/worker/src/rate_limit.rs b/worker/src/rate_limit.rs new file mode 100644 index 00000000..5f381fa9 --- /dev/null +++ b/worker/src/rate_limit.rs @@ -0,0 +1,67 @@ +use crate::{send::SendFuture, EnvBinding, Result}; +use serde::{Deserialize, Serialize}; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_futures::JsFuture; +use worker_sys::RateLimiter as RateLimiterSys; + +#[derive(Debug)] +pub struct RateLimiter(RateLimiterSys); + +#[derive(Serialize, Deserialize)] +struct RateLimitOptions { + key: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RateLimitOutcome { + pub success: bool, +} + +unsafe impl Send for RateLimiter {} +unsafe impl Sync for RateLimiter {} + +impl EnvBinding for RateLimiter { + const TYPE_NAME: &'static str = "Ratelimit"; +} +impl RateLimiter { + pub async fn limit(&self, key: String) -> Result { + let arg = serde_wasm_bindgen::to_value(&RateLimitOptions { key })?; + let promise = self.0.limit(arg.into())?; + let fut = SendFuture::new(JsFuture::from(promise)); + let result = fut.await?; + let outcome = serde_wasm_bindgen::from_value(result)?; + Ok(outcome) + } +} + +impl JsCast for RateLimiter { + fn instanceof(val: &JsValue) -> bool { + val.is_instance_of::() + } + + fn unchecked_from_js(val: JsValue) -> Self { + Self(val.into()) + } + + fn unchecked_from_js_ref(val: &JsValue) -> &Self { + unsafe { &*(val as *const JsValue as *const Self) } + } +} + +impl From for JsValue { + fn from(limiter: RateLimiter) -> Self { + JsValue::from(limiter.0) + } +} + +impl AsRef for RateLimiter { + fn as_ref(&self) -> &JsValue { + &self.0 + } +} + +impl From for RateLimiter { + fn from(inner: RateLimiterSys) -> Self { + Self(inner) + } +} diff --git a/worker/src/request.rs b/worker/src/request.rs index 25c237f6..6be8a7b8 100644 --- a/worker/src/request.rs +++ b/worker/src/request.rs @@ -5,8 +5,11 @@ use crate::{ }; use serde::de::DeserializeOwned; +#[cfg(test)] use std::borrow::Cow; -use url::{form_urlencoded::Parse, Url}; +#[cfg(test)] +use url::form_urlencoded::Parse; +use url::Url; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use worker_sys::ext::RequestExt; @@ -83,21 +86,20 @@ impl TryFrom<&Request> for web_sys::Request { impl Request { /// Construct a new `Request` with an HTTP Method. pub fn new(uri: &str, method: Method) -> Result { - web_sys::Request::new_with_str_and_init( - uri, - web_sys::RequestInit::new().method(method.as_ref()), - ) - .map(|req| { - let mut req: Request = req.into(); - req.immutable = false; - req - }) - .map_err(|e| { - Error::JsError( - e.as_string() - .unwrap_or_else(|| "invalid URL or method for Request".to_string()), - ) - }) + let init = web_sys::RequestInit::new(); + init.set_method(method.as_ref()); + web_sys::Request::new_with_str_and_init(uri, &init) + .map(|req| { + let mut req: Request = req.into(); + req.immutable = false; + req + }) + .map_err(|e| { + Error::JsError( + e.as_string() + .unwrap_or_else(|| "invalid URL or method for Request".to_string()), + ) + }) } /// Construct a new `Request` with a `RequestInit` configuration. @@ -290,11 +292,13 @@ impl Request { } } +#[cfg(test)] pub struct ParamIter<'a> { inner: Parse<'a>, key: &'a str, } +#[cfg(test)] impl<'a> Iterator for ParamIter<'a> { type Item = Cow<'a, str>; diff --git a/worker/src/request_init.rs b/worker/src/request_init.rs index e7ae9651..74169512 100644 --- a/worker/src/request_init.rs +++ b/worker/src/request_init.rs @@ -5,9 +5,10 @@ use crate::http::Method; use js_sys::{self, Object}; use serde::Serialize; -use wasm_bindgen::{prelude::*, JsValue}; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; /// Optional options struct that contains settings to apply to the `Request`. +#[derive(Debug)] pub struct RequestInit { /// Currently requires a manual conversion from your data into a [`wasm_bindgen::JsValue`]. pub body: Option, @@ -57,11 +58,13 @@ impl RequestInit { impl From<&RequestInit> for web_sys::RequestInit { fn from(req: &RequestInit) -> Self { - let mut inner = web_sys::RequestInit::new(); - inner.headers(req.headers.as_ref()); - inner.method(req.method.as_ref()); - inner.redirect(req.redirect.into()); - inner.body(req.body.as_ref()); + let inner = web_sys::RequestInit::new(); + inner.set_headers(req.headers.as_ref()); + inner.set_method(req.method.as_ref()); + inner.set_redirect(req.redirect.into()); + if let Some(body) = req.body.as_ref() { + inner.set_body(body); + } // set the Cloudflare-specific `cf` property on FFI RequestInit let r = ::js_sys::Reflect::set( @@ -92,6 +95,7 @@ impl Default for RequestInit { } /// +#[derive(Debug)] pub struct CfProperties { /// Whether Cloudflare Apps should be enabled for this request. Defaults to `true`. pub apps: Option, @@ -115,6 +119,8 @@ pub struct CfProperties { /// zero and negative integers. A value of 0 indicates that the cache asset expires immediately. /// Any negative value instructs Cloudflare not to cache at all. pub cache_ttl_by_status: Option>, + /// Enables Image Resizing for this request. + pub image: Option, /// Enables or disables AutoMinify for various file types. /// For example: `{ javascript: true, css: true, html: false }`. pub minify: Option, @@ -208,11 +214,12 @@ impl From<&CfProperties> for JsValue { &JsValue::from(props.mirage.unwrap_or(defaults.mirage.unwrap_or_default())), ); - let polish_val: &str = props - .polish - .unwrap_or(defaults.polish.unwrap_or_default()) - .into(); - set_prop(&obj, &JsValue::from("polish"), &JsValue::from(polish_val)); + let polish_val = props.polish.unwrap_or(defaults.polish.unwrap_or_default()); + set_prop( + &obj, + &JsValue::from("polish"), + &serde_wasm_bindgen::to_value(&polish_val).unwrap(), + ); set_prop( &obj, @@ -235,6 +242,14 @@ impl From<&CfProperties> for JsValue { ), ); + if let Some(image) = &props.image { + set_prop( + &obj, + &JsValue::from("image"), + &serde_wasm_bindgen::to_value(&image).unwrap(), + ); + } + obj.into() } } @@ -264,6 +279,7 @@ impl Default for CfProperties { cache_ttl_by_status: None, minify: None, mirage: Some(true), + image: None, polish: None, resolve_override: None, scrape_shield: Some(true), @@ -274,7 +290,8 @@ impl Default for CfProperties { /// Configuration options for Cloudflare's minification features: /// #[wasm_bindgen] -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Debug, Default, Serialize)] +#[serde(rename_all = "kebab-case")] pub struct MinifyConfig { pub js: bool, pub html: bool, @@ -283,32 +300,218 @@ pub struct MinifyConfig { /// Configuration options for Cloudflare's image optimization feature: /// -#[wasm_bindgen] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Default, Serialize)] +#[serde(rename_all = "kebab-case")] pub enum PolishConfig { + #[default] Off, Lossy, Lossless, } -impl Default for PolishConfig { - fn default() -> Self { - Self::Off - } +#[derive(Clone, Debug, serde::Serialize)] +#[serde(untagged)] +pub enum ResizeBorder { + Uniform { + color: String, + width: usize, + }, + Varying { + color: String, + top: usize, + right: usize, + bottom: usize, + left: usize, + }, } -impl From for &str { - fn from(conf: PolishConfig) -> Self { - match conf { - PolishConfig::Off => "off", - PolishConfig::Lossy => "lossy", - PolishConfig::Lossless => "lossless", - } - } +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeOriginAuth { + SharePublicly, } -#[wasm_bindgen] -#[derive(Default, Clone, Copy)] +/// Configuration options for Cloudflare's image resizing feature: +/// +#[derive(Clone, Debug, Default, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct ResizeConfig { + /// Whether to preserve animation frames from input files. Default is `true`. Setting it to `false` reduces animations to still images. + pub anim: Option, + /// Background color to add underneath the image. Applies to images with transparency (for example, PNG) and images resized with `fit=pad`. + pub background: Option, + /// Blur radius between `1` (slight blur) and `250` (maximum). Be aware that you cannot use this option to reliably obscure image content. + pub blur: Option, + /// Adds a border around the image. The border is added after resizing. + pub border: Option, + /// Increase brightness by a factor. A value of `1.0` equals no change, a value of `0.5` equals half brightness, and a value of `2.0` equals twice as bright. + pub brightness: Option, + /// Slightly reduces latency on a cache miss by selecting a quickest-to-compress file format, at a cost of increased file size and lower image quality. + pub compression: Option, + /// Increase contrast by a factor. A value of `1.0` equals no change, a value of `0.5` equals low contrast, and a value of `2.0` equals high contrast. + pub contrast: Option, + /// Device Pixel Ratio. Default is `1`. Multiplier for `width`/`height` that makes it easier to specify higher-DPI sizes in ``. + pub dpr: Option, + /// Drawing operations to overlay on the image. + pub draw: Option, + /// Affects interpretation of `width` and `height`. All resizing modes preserve aspect ratio. + pub fit: Option, + /// Flips the image horizontally, vertically, or both. Can be used with the `rotate` parameter to set the orientation of an image. + pub flip: Option, + /// The `auto` option will serve the WebP or AVIF format to browsers that support it. If this option is not specified, a standard format like JPEG or PNG will be used. + pub format: Option, + /// Increase exposure by a factor. A value of `1.0` equals no change, a value of `0.5` darkens the image, and a value of `2.0` lightens the image. + pub gamma: Option, + /// Specifies how an image should be cropped when used with `fit=cover` and `fit=crop`. Available options are `auto`, `face`, a side (`left`, `right`, `top`, `bottom`), and relative coordinates (`XxY` with a valid range of `0.0` to `1.0`). + pub gravity: Option, + /// Specifies maximum height of the image in pixels. Exact behavior depends on the `fit` mode (described below). + pub height: Option, + /// Controls amount of invisible metadata (EXIF data) that should be preserved. Color profiles and EXIF rotation are applied to the image even if the metadata is discarded. + pub metadata: Option, + /// Authentication method for accessing the origin image. + pub origin_auth: Option, + /// In case of a fatal error that prevents the image from being resized, redirects to the unresized source image URL. This may be useful in case some images require user authentication and cannot be fetched anonymously via Worker. + pub onerror: Option, + /// Specifies quality for images in JPEG, WebP, and AVIF formats. The quality is in a 1-100 scale, but useful values are between `50` (low quality, small file size) and `90` (high quality, large file size). + pub quality: Option, + /// Number of degrees (`90`, `180`, or `270`) to rotate the image by. `width` and `height` options refer to axes after rotation. + pub rotate: Option, + /// Increases saturation by a factor. A value of `1.0` equals no change, a value of `0.5` equals half saturation, and a value of `2.0` equals twice as saturated. + pub saturation: Option, + /// Specifies strength of sharpening filter to apply to the image. The value is a floating-point number between `0` (no sharpening, default) and `10` (maximum). + pub sharpen: Option, + /// Specifies a number of pixels to cut off on each side. Allows removal of borders or cutting out a specific fragment of an image. + pub trim: Option, + /// Specifies maximum width of the image. Exact behavior depends on the `fit` mode; use the `fit=scale-down` option to ensure that the image will not be enlarged unnecessarily. + pub width: Option, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeCompression { + Fast, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(untagged)] +pub enum ResizeDrawRepeat { + Uniform(bool), + Axis(String), +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct ResizeDraw { + url: String, + opacity: Option, + repeat: Option, + top: Option, + bottom: Option, + left: Option, + right: Option, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeFit { + ScaleDown, + Contain, + Cover, + Crop, + Pad, +} + +#[derive(Clone, Copy, Debug, Serialize)] +pub enum ResizeFlip { + #[serde(rename = "h")] + Horizontally, + #[serde(rename = "v")] + Vertically, + #[serde(rename = "hv")] + Both, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeFormat { + Avif, + Webp, + Json, + Jpeg, + Png, + BaselineJpeg, + PngForce, + Svg, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeGravitySide { + Auto, + Left, + Right, + Top, + Bottom, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +#[serde(untagged)] +pub enum ResizeGravity { + Side(ResizeGravitySide), + Coords { x: f64, y: f64 }, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeQualityLiteral { + Low, + MediumLow, + MediumHigh, + High, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +#[serde(untagged)] +pub enum ResizeQuality { + Literal(ResizeQualityLiteral), + Specific { value: usize }, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeMetadata { + Keep, + Copyright, + None, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ResizeOnerror { + Redirect, +} + +#[derive(Clone, Copy, Debug, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct ResizeTrim { + #[serde(skip_serializing_if = "Option::is_none")] + pub top: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bottom: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub left: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub right: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub width: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub height: Option, +} + +#[derive(Clone, Copy, Debug, Default, Serialize)] +#[serde(rename_all = "kebab-case")] pub enum RequestRedirect { Error, #[default] diff --git a/worker/src/response.rs b/worker/src/response.rs index 2ec3bdfb..4ffac586 100644 --- a/worker/src/response.rs +++ b/worker/src/response.rs @@ -376,7 +376,7 @@ impl ResponseBuilder { } /// Set a single header on this response. - pub fn with_header(mut self, key: &str, value: &str) -> Result { + pub fn with_header(self, key: &str, value: &str) -> Result { self.headers.set(key, value)?; Ok(self) } @@ -458,7 +458,7 @@ impl ResponseBuilder { /// Create a `Response` using `B` as the body encoded as JSON. Sets the associated /// `Content-Type` header for the `Response` as `application/json`. - pub fn from_json(mut self, value: &B) -> Result { + pub fn from_json(self, value: &B) -> Result { if let Ok(data) = serde_json::to_string(value) { self.headers.set(CONTENT_TYPE, "application/json")?; Ok(self.fixed(data.into_bytes())) @@ -469,7 +469,7 @@ impl ResponseBuilder { /// Create a `Response` using the body encoded as HTML. Sets the associated `Content-Type` /// header for the `Response` as `text/html; charset=utf-8`. - pub fn from_html(mut self, html: impl AsRef) -> Result { + pub fn from_html(self, html: impl AsRef) -> Result { self.headers.set(CONTENT_TYPE, "text/html; charset=utf-8")?; let data = html.as_ref().as_bytes().to_vec(); Ok(self.fixed(data)) @@ -477,7 +477,7 @@ impl ResponseBuilder { /// Create a `Response` using unprocessed bytes provided. Sets the associated `Content-Type` /// header for the `Response` as `application/octet-stream`. - pub fn from_bytes(mut self, bytes: Vec) -> Result { + pub fn from_bytes(self, bytes: Vec) -> Result { self.headers.set(CONTENT_TYPE, "application/octet-stream")?; Ok(self.fixed(bytes)) } @@ -510,7 +510,7 @@ impl ResponseBuilder { /// Create a `Response` using unprocessed text provided. Sets the associated `Content-Type` /// header for the `Response` as `text/plain; charset=utf-8`. - pub fn ok(mut self, body: impl Into) -> Result { + pub fn ok(self, body: impl Into) -> Result { self.headers .set(CONTENT_TYPE, "text/plain; charset=utf-8")?; @@ -533,8 +533,8 @@ impl ResponseBuilder { impl From for web_sys::ResponseInit { fn from(init: ResponseBuilder) -> Self { let mut edge_init = web_sys::ResponseInit::new(); - edge_init.status(init.status_code); - edge_init.headers(&init.headers.0); + edge_init.set_status(init.status_code); + edge_init.set_headers(&init.headers.0); if let Some(websocket) = &init.websocket { edge_init .websocket(websocket.as_ref()) diff --git a/worker/src/router.rs b/worker/src/router.rs index 8c7699bd..8a52cfb3 100644 --- a/worker/src/router.rs +++ b/worker/src/router.rs @@ -2,15 +2,15 @@ use std::{collections::HashMap, future::Future, rc::Rc}; use futures_util::future::LocalBoxFuture; use matchit::{Match, Router as MatchItRouter}; -use worker_kv::KvStore; use crate::{ durable::ObjectNamespace, env::{Env, Secret, Var}, http::Method, + rate_limit::RateLimiter, request::Request, response::Response, - Bucket, Fetcher, Result, + Bucket, Fetcher, KvStore, Result, }; type HandlerFn = fn(Request, RouteContext) -> Result; @@ -19,6 +19,7 @@ type AsyncHandlerFn<'a, D> = /// Represents the URL parameters parsed from the path, e.g. a route with "/user/:id" pattern would /// contain a single "id" key. +#[derive(Debug)] pub struct RouteParams(HashMap); impl RouteParams { @@ -48,8 +49,15 @@ pub struct Router<'a, D> { data: D, } +impl core::fmt::Debug for Router<'_, ()> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Router").finish() + } +} + /// Container for a route's parsed parameters, data, and environment bindings from the Runtime (such /// as KV Stores, Durable Objects, Variables, and Secrets). +#[derive(Debug)] pub struct RouteContext { pub data: D, pub env: Env, @@ -111,9 +119,14 @@ impl RouteContext { pub fn d1(&self, binding: &str) -> Result { self.env.d1(binding) } + + /// Access a Rate Limiter by the binding name configured in your wrangler.toml file. + pub fn rate_limiter(&self, binding: &str) -> Result { + self.env.rate_limiter(binding) + } } -impl<'a> Router<'a, ()> { +impl Router<'_, ()> { /// Construct a new `Router`. Or, call `Router::with_data(D)` to add arbitrary data that will be /// available to your various routes. pub fn new() -> Self { @@ -173,6 +186,12 @@ impl<'a, D: 'a> Router<'a, D> { self } + /// Register an HTTP handler that will exclusively respond to REPORT requests. + pub fn report(mut self, pattern: &str, func: HandlerFn) -> Self { + self.add_handler(pattern, Handler::Sync(func), vec![Method::Report]); + self + } + /// Register an HTTP handler that will respond to any requests. pub fn on(mut self, pattern: &str, func: HandlerFn) -> Self { self.add_handler(pattern, Handler::Sync(func), Method::all()); @@ -190,7 +209,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will exclusively respond to HEAD requests. Enables the use of /// `async/await` syntax in the callback. - pub fn head_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn head_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -204,7 +227,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will exclusively respond to GET requests. Enables the use of /// `async/await` syntax in the callback. - pub fn get_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn get_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -218,7 +245,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will exclusively respond to POST requests. Enables the use of /// `async/await` syntax in the callback. - pub fn post_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn post_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -232,7 +263,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will exclusively respond to PUT requests. Enables the use of /// `async/await` syntax in the callback. - pub fn put_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn put_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -246,7 +281,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will exclusively respond to PATCH requests. Enables the use of /// `async/await` syntax in the callback. - pub fn patch_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn patch_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -260,7 +299,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will exclusively respond to DELETE requests. Enables the use /// of `async/await` syntax in the callback. - pub fn delete_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn delete_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -277,7 +320,7 @@ impl<'a, D: 'a> Router<'a, D> { pub fn options_async( mut self, pattern: &str, - func: fn(Request, RouteContext) -> T, + func: impl Fn(Request, RouteContext) -> T + 'a, ) -> Self where T: Future> + 'a, @@ -292,7 +335,11 @@ impl<'a, D: 'a> Router<'a, D> { /// Register an HTTP handler that will respond to any requests. Enables the use of `async/await` /// syntax in the callback. - pub fn on_async(mut self, pattern: &str, func: fn(Request, RouteContext) -> T) -> Self + pub fn on_async( + mut self, + pattern: &str, + func: impl Fn(Request, RouteContext) -> T + 'a, + ) -> Self where T: Future> + 'a, { @@ -309,7 +356,7 @@ impl<'a, D: 'a> Router<'a, D> { pub fn or_else_any_method_async( mut self, pattern: &str, - func: fn(Request, RouteContext) -> T, + func: impl Fn(Request, RouteContext) -> T + 'a, ) -> Self where T: Future> + 'a, diff --git a/worker/src/schedule.rs b/worker/src/schedule.rs index 33afc1bb..83002471 100644 --- a/worker/src/schedule.rs +++ b/worker/src/schedule.rs @@ -38,7 +38,7 @@ impl ScheduledEvent { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ScheduleContext { edge: EdgeScheduleContext, } diff --git a/worker/src/secret_store.rs b/worker/src/secret_store.rs new file mode 100644 index 00000000..e5103844 --- /dev/null +++ b/worker/src/secret_store.rs @@ -0,0 +1,82 @@ +use crate::{ + send::{SendFuture, SendWrapper}, + EnvBinding, Fetcher, Result, +}; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::JsFuture; + +/// A binding to a Cloudflare Secret Store. +#[derive(Debug, Clone)] +pub struct SecretStore(SendWrapper); + +// Allows for attachment to axum router, as Workers will never allow multithreading. +unsafe impl Send for SecretStore {} +unsafe impl Sync for SecretStore {} + +impl EnvBinding for SecretStore { + const TYPE_NAME: &'static str = "Fetcher"; +} + +impl JsCast for SecretStore { + fn instanceof(val: &wasm_bindgen::JsValue) -> bool { + Fetcher::instanceof(val) + } + + fn unchecked_from_js(val: wasm_bindgen::JsValue) -> Self { + let fetcher = Fetcher::unchecked_from_js(val); + Self::from(fetcher) + } + + fn unchecked_from_js_ref(val: &wasm_bindgen::JsValue) -> &Self { + unsafe { &*(val as *const wasm_bindgen::JsValue as *const Self) } + } +} + +impl AsRef for SecretStore { + fn as_ref(&self) -> &wasm_bindgen::JsValue { + self.0.as_ref() + } +} + +impl From for SecretStore { + fn from(val: wasm_bindgen::JsValue) -> Self { + Self::unchecked_from_js(val) + } +} + +impl From for SecretStore { + fn from(fetcher: Fetcher) -> Self { + Self(SendWrapper::new(fetcher.into_rpc())) + } +} + +impl From for wasm_bindgen::JsValue { + fn from(secret_store: SecretStore) -> Self { + let sys_obj: &worker_sys::SecretStoreSys = secret_store.0.as_ref(); + sys_obj.clone().into() + } +} + +impl SecretStore { + /// Get a secret value from the secret store. + /// Returns None if the secret doesn't exist. + pub async fn get(&self) -> Result> { + let promise = match self.0.get() { + Ok(p) => p, + Err(_) => return Ok(None), // Secret not found + }; + + let fut = SendFuture::new(JsFuture::from(promise)); + + let output = match fut.await { + Ok(val) => val, + Err(_) => return Ok(None), // Promise rejected, secret not found + }; + + if output.is_null() || output.is_undefined() { + Ok(None) + } else { + Ok(Some(::serde_wasm_bindgen::from_value(output)?)) + } + } +} diff --git a/worker/src/send.rs b/worker/src/send.rs index 074f354b..2e9fe975 100644 --- a/worker/src/send.rs +++ b/worker/src/send.rs @@ -11,6 +11,7 @@ use std::pin::Pin; use std::task::Context; use std::task::Poll; +#[derive(Debug)] #[pin_project] /// Wrap any future to make it `Send`. /// diff --git a/worker/src/socket.rs b/worker/src/socket.rs index a485eb14..6ce0c94c 100644 --- a/worker/src/socket.rs +++ b/worker/src/socket.rs @@ -1,6 +1,5 @@ use std::{ convert::TryFrom, - io::ErrorKind, pin::Pin, task::{Context, Poll}, }; @@ -41,7 +40,7 @@ impl TryFrom for SocketInfo { } } -#[derive(Default)] +#[derive(Debug, Default)] enum Reading { #[default] None, @@ -49,14 +48,14 @@ enum Reading { Ready(Vec), } -#[derive(Default)] +#[derive(Debug, Default)] enum Writing { Pending(JsFuture, WritableStreamDefaultWriter, usize), #[default] None, } -#[derive(Default)] +#[derive(Debug, Default)] enum Closing { Pending(JsFuture), #[default] @@ -64,6 +63,7 @@ enum Closing { } /// Represents an outbound TCP connection from your Worker. +#[derive(Debug)] pub struct Socket { inner: worker_sys::Socket, writable: WritableStream, @@ -148,9 +148,9 @@ fn js_value_to_std_io_error(value: JsValue) -> IoError { } else if let Some(value) = value.dyn_ref::() { value.to_string().into() } else { - format!("Error interpreting JsError: {:?}", value) + format!("Error interpreting JsError: {value:?}") }; - IoError::new(ErrorKind::Other, s) + IoError::other(s) } impl AsyncRead for Socket { fn poll_read( @@ -172,11 +172,8 @@ impl AsyncRead for Socket { let done: JsBoolean = match Reflect::get(&value, &JsValue::from("done")) { Ok(value) => value.into(), Err(error) => { - let msg = format!("Unable to interpret field 'done' in ReadableStreamDefaultReader.read(): {:?}", error); - return ( - Reading::None, - Poll::Ready(Err(IoError::new(ErrorKind::Other, msg))), - ); + let msg = format!("Unable to interpret field 'done' in ReadableStreamDefaultReader.read(): {error:?}"); + return (Reading::None, Poll::Ready(Err(IoError::other(msg)))); } }; if done.is_truthy() { @@ -188,11 +185,8 @@ impl AsyncRead for Socket { ) { Ok(value) => value.into(), Err(error) => { - let msg = format!("Unable to interpret field 'value' in ReadableStreamDefaultReader.read(): {:?}", error); - return ( - Reading::None, - Poll::Ready(Err(IoError::new(ErrorKind::Other, msg))), - ); + let msg = format!("Unable to interpret field 'value' in ReadableStreamDefaultReader.read(): {error:?}"); + return (Reading::None, Poll::Ready(Err(IoError::other(msg)))); } }; let data = arr.to_vec(); @@ -211,10 +205,9 @@ impl AsyncRead for Socket { Ok(reader) => reader, Err(error) => { let msg = format!( - "Unable to cast JsObject to ReadableStreamDefaultReader: {:?}", - error + "Unable to cast JsObject to ReadableStreamDefaultReader: {error:?}" ); - return Poll::Ready(Err(IoError::new(ErrorKind::Other, msg))); + return Poll::Ready(Err(IoError::other(msg))); } }; @@ -240,8 +233,8 @@ impl AsyncWrite for Socket { let writer: WritableStreamDefaultWriter = match self.writable.get_writer() { Ok(writer) => writer, Err(error) => { - let msg = format!("Could not retrieve Writer: {:?}", error); - return Poll::Ready(Err(IoError::new(ErrorKind::Other, msg))); + let msg = format!("Could not retrieve Writer: {error:?}"); + return Poll::Ready(Err(IoError::other(msg))); } }; Self::handle_write_future( @@ -291,6 +284,7 @@ impl AsyncWrite for Socket { } /// Secure transport options for outbound TCP connections. +#[derive(Debug, Clone)] pub enum SecureTransport { /// Do not use TLS. Off, @@ -302,6 +296,7 @@ pub enum SecureTransport { } /// Used to configure outbound TCP connections. +#[derive(Debug, Clone)] pub struct SocketOptions { /// Specifies whether or not to use TLS when creating the TCP socket. pub secure_transport: SecureTransport, @@ -322,6 +317,7 @@ impl Default for SocketOptions { } /// The host and port that you wish to connect to. +#[derive(Debug, Clone)] pub struct SocketAddress { /// The hostname to connect to. Example: `cloudflare.com`. pub hostname: String, @@ -329,7 +325,7 @@ pub struct SocketAddress { pub port: u16, } -#[derive(Default)] +#[derive(Default, Debug, Clone)] pub struct ConnectionBuilder { options: SocketOptions, } @@ -411,6 +407,7 @@ pub mod postgres_tls { /// .connect("database_url", 5432)?; /// let _ = config.connect_raw(socket, PassthroughTls).await?; /// ``` + #[derive(Debug, Clone, Default)] pub struct PassthroughTls; #[derive(Debug)] diff --git a/worker/src/sql.rs b/worker/src/sql.rs new file mode 100644 index 00000000..50c875a9 --- /dev/null +++ b/worker/src/sql.rs @@ -0,0 +1,415 @@ +use js_sys::Array; +use std::convert::TryFrom; +use wasm_bindgen::{JsCast, JsValue}; +use worker_sys::types::{SqlStorage as SqlStorageSys, SqlStorageCursor as SqlStorageCursorSys}; + +use serde::de::DeserializeOwned; +use serde_wasm_bindgen as swb; + +use crate::Error; +use crate::Result; + +/// A value that can be stored in Durable Object SQL storage. +/// +/// This enum represents the types that can be safely passed to SQL queries +/// while maintaining type safety and proper conversion to JavaScript values. +#[derive(Debug, Clone, PartialEq)] +pub enum SqlStorageValue { + /// SQL NULL value + Null, + /// Boolean value + Boolean(bool), + /// 64-bit signed integer + /// Precision may be lost if outside JavaScript safe integer range. Use `try_from_i64` to ensure safety. + Integer(i64), + /// 64-bit floating point number + Float(f64), + /// UTF-8 string + String(String), + /// Binary data + Blob(Vec), +} + +// From implementations for convenient conversion from Rust types +impl From for SqlStorageValue { + fn from(value: bool) -> Self { + SqlStorageValue::Boolean(value) + } +} + +impl From for SqlStorageValue { + fn from(value: i32) -> Self { + SqlStorageValue::Integer(value as i64) + } +} + +impl From for SqlStorageValue { + fn from(value: i64) -> Self { + SqlStorageValue::Integer(value) + } +} + +impl SqlStorageValue { + /// Create a new Integer value, checking if it's within JavaScript safe integer bounds. + /// + /// Returns an error if the value is outside the range that can be safely represented + /// in JavaScript (±2^53 - 1). + pub fn try_from_i64(value: i64) -> Result { + if value >= js_sys::Number::MIN_SAFE_INTEGER as i64 + && value <= js_sys::Number::MAX_SAFE_INTEGER as i64 + { + Ok(SqlStorageValue::Integer(value)) + } else { + Err(crate::Error::from( + "Value outside JavaScript safe integer range", + )) + } + } +} + +impl From for SqlStorageValue { + fn from(value: f64) -> Self { + SqlStorageValue::Float(value) + } +} + +impl From for SqlStorageValue { + fn from(value: String) -> Self { + SqlStorageValue::String(value) + } +} + +impl From<&str> for SqlStorageValue { + fn from(value: &str) -> Self { + SqlStorageValue::String(value.to_string()) + } +} + +impl From> for SqlStorageValue { + fn from(value: Vec) -> Self { + SqlStorageValue::Blob(value) + } +} + +impl From> for SqlStorageValue +where + T: Into, +{ + fn from(value: Option) -> Self { + match value { + Some(v) => v.into(), + None => SqlStorageValue::Null, + } + } +} + +// Convert SqlStorageValue to JsValue for passing to JavaScript +impl From for JsValue { + fn from(val: SqlStorageValue) -> Self { + match val { + SqlStorageValue::Null => JsValue::NULL, + SqlStorageValue::Boolean(b) => JsValue::from(b), + SqlStorageValue::Integer(i) => { + let js_value = JsValue::from(i as f64); + + if !js_sys::Number::is_safe_integer(&js_value) { + crate::console_debug!( + "Warning: Converting {} to JsValue as Integer, \ + but it is outside the JavaScript safe-integer range", + i + ); + } + js_value + } + SqlStorageValue::Float(f) => JsValue::from(f), + SqlStorageValue::String(s) => JsValue::from(s), + SqlStorageValue::Blob(bytes) => { + // Convert Vec to Uint8Array for JavaScript + let array = js_sys::Uint8Array::new_with_length(bytes.len() as u32); + array.copy_from(&bytes); + array.into() + } + } + } +} + +impl TryFrom for SqlStorageValue { + type Error = crate::Error; + + fn try_from(js_val: JsValue) -> Result { + if js_val.is_null() || js_val.is_undefined() { + Ok(SqlStorageValue::Null) + } else if let Some(bool_val) = js_val.as_bool() { + Ok(SqlStorageValue::Boolean(bool_val)) + } else if let Some(str_val) = js_val.as_string() { + Ok(SqlStorageValue::String(str_val)) + } else if let Some(num_val) = js_val.as_f64() { + if js_sys::Number::is_safe_integer(&js_val) { + Ok(SqlStorageValue::Integer(num_val as i64)) + } else { + Ok(SqlStorageValue::Float(num_val)) + } + } else { + js_val + .dyn_into::() + .map(|uint8_array| { + let mut bytes = vec![0u8; uint8_array.length() as usize]; + uint8_array.copy_to(&mut bytes); + SqlStorageValue::Blob(bytes) + }) + .or_else(|js_val| { + js_val + .dyn_into::() + .map(|array_buffer| { + let uint8_array = js_sys::Uint8Array::new(&array_buffer); + let mut bytes = vec![0u8; uint8_array.length() as usize]; + uint8_array.copy_to(&mut bytes); + SqlStorageValue::Blob(bytes) + }) + }) + .map_err(|_| Error::from("Unsupported JavaScript value type")) + } + } +} + +/// Wrapper around the Workers `SqlStorage` interface exposed at `ctx.storage.sql`. +/// +/// The API is intentionally minimal for now – additional helper utilities can be built on top +/// as needed. +#[derive(Clone, Debug)] +pub struct SqlStorage { + inner: SqlStorageSys, +} + +unsafe impl Send for SqlStorage {} +unsafe impl Sync for SqlStorage {} + +impl SqlStorage { + pub(crate) fn new(inner: SqlStorageSys) -> Self { + Self { inner } + } + + /// Size of the underlying SQLite database in bytes. + pub fn database_size(&self) -> usize { + self.inner.database_size() as usize + } + + /// Execute a SQL query and return a cursor over the results. + /// + /// `bindings` correspond to positional `?` placeholders in the query. + /// Accepts `SqlStorageValue` for type-safe parameter binding. + pub fn exec( + &self, + query: &str, + bindings: impl Into>>, + ) -> Result { + let array = Array::new(); + if let Some(bindings) = bindings.into() { + for v in bindings { + array.push(&v.into()); + } + } + let cursor = self.inner.exec(query, array).map_err(Error::from)?; + Ok(SqlCursor { inner: cursor }) + } + + /// Execute a SQL query with raw JavaScript values. + /// + /// This method provides direct access to `JsValue` parameters for advanced use cases + /// where you need more control over the JavaScript conversion. + pub fn exec_raw( + &self, + query: &str, + bindings: impl Into>>, + ) -> Result { + let array = Array::new(); + if let Some(bindings) = bindings.into() { + for v in bindings { + array.push(&v); + } + } + let cursor = self.inner.exec(query, array).map_err(Error::from)?; + Ok(SqlCursor { inner: cursor }) + } +} + +impl AsRef for SqlStorage { + fn as_ref(&self) -> &JsValue { + &self.inner + } +} + +/// A cursor returned from `SqlStorage::exec`. +#[derive(Clone, Debug)] +pub struct SqlCursor { + inner: SqlStorageCursorSys, +} + +unsafe impl Send for SqlCursor {} +unsafe impl Sync for SqlCursor {} + +/// Iterator wrapper for typed cursor results. +/// +/// This iterator yields deserialized rows of type `T`, providing a type-safe +/// way to iterate over SQL query results with automatic conversion to Rust types. +#[derive(Debug)] +pub struct SqlCursorIterator { + cursor: SqlCursor, + _phantom: std::marker::PhantomData, +} + +impl Iterator for SqlCursorIterator +where + T: DeserializeOwned, +{ + type Item = Result; + + fn next(&mut self) -> Option { + let result = self.cursor.inner.next(); + + let done = js_sys::Reflect::get(&result, &JsValue::from("done")) + .ok() + .and_then(|v| v.as_bool()) + .unwrap_or(true); + + if done { + None + } else { + let value = js_sys::Reflect::get(&result, &JsValue::from("value")) + .map_err(Error::from) + .and_then(|js_val| swb::from_value(js_val).map_err(Error::from)); + Some(value) + } + } +} + +/// Iterator wrapper for raw cursor results as Vec. +/// +/// This iterator yields raw values as `Vec`, providing efficient +/// access to SQL data without deserialization overhead. Useful when you need +/// direct access to the underlying SQL values without column names. +#[derive(Debug)] +pub struct SqlCursorRawIterator { + inner: js_sys::Iterator, +} + +impl Iterator for SqlCursorRawIterator { + type Item = Result>; + + fn next(&mut self) -> Option { + match self.inner.next() { + Ok(iterator_next) => { + if iterator_next.done() { + None + } else { + let js_val = iterator_next.value(); + let array_result = js_array_to_sql_storage_values(js_val); + Some(array_result) + } + } + Err(e) => Some(Err(Error::from(e))), + } + } +} + +fn js_array_to_sql_storage_values(js_val: JsValue) -> Result> { + let array = js_sys::Array::from(&js_val); + let mut values = Vec::with_capacity(array.length() as usize); + + for i in 0..array.length() { + let item = array.get(i); + let sql_value = SqlStorageValue::try_from(item)?; + values.push(sql_value); + } + + Ok(values) +} + +impl SqlCursor { + /// Consume the remaining rows of the cursor into a `Vec` of deserialised objects. + pub fn to_array(&self) -> Result> + where + T: DeserializeOwned, + { + let arr = self.inner.to_array(); + let mut out = Vec::with_capacity(arr.length() as usize); + for val in arr.iter() { + out.push(swb::from_value(val)?); + } + Ok(out) + } + + /// Return the first (and only) row of the query result. + pub fn one(&self) -> Result + where + T: DeserializeOwned, + { + let val = self.inner.one(); + Ok(swb::from_value(val)?) + } + + /// Column names returned by the query. + pub fn column_names(&self) -> Vec { + self.inner + .column_names() + .iter() + .map(|v| v.as_string().unwrap_or_default()) + .collect() + } + + /// Number of rows read so far by the cursor. + pub fn rows_read(&self) -> usize { + self.inner.rows_read() as usize + } + + /// Number of rows written by the query so far. + pub fn rows_written(&self) -> usize { + self.inner.rows_written() as usize + } + + /// Returns a Rust iterator that yields deserialized rows of type T. + /// + /// This provides a first-class Rust API for iterating over query results + /// with proper type safety and error handling. + pub fn next(&self) -> SqlCursorIterator + where + T: DeserializeOwned, + { + SqlCursorIterator { + cursor: self.clone(), + _phantom: std::marker::PhantomData, + } + } + + /// Returns a Rust iterator where each row is a Vec. + /// + /// This method provides a more efficient way to iterate over results when you + /// only need the raw values without column names, using proper Rust types. + pub fn raw(&self) -> SqlCursorRawIterator { + SqlCursorRawIterator { + inner: self.inner.raw(), + } + } +} + +impl Iterator for SqlCursor { + type Item = Result; + + fn next(&mut self) -> Option { + let result = self.inner.next(); + + // Extract 'done' property from iterator result + let done = js_sys::Reflect::get(&result, &JsValue::from("done")) + .ok() + .and_then(|v| v.as_bool()) + .unwrap_or(true); + + if done { + None + } else { + // Extract 'value' property from iterator result + let value = js_sys::Reflect::get(&result, &JsValue::from("value")).map_err(Error::from); + Some(value) + } + } +} diff --git a/worker/src/streams.rs b/worker/src/streams.rs index 6d7eda78..90f982fd 100644 --- a/worker/src/streams.rs +++ b/worker/src/streams.rs @@ -47,6 +47,15 @@ pub struct FixedLengthStream { inner: Pin>> + 'static>>, } +impl core::fmt::Debug for FixedLengthStream { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("FixedLengthStream") + .field("length", &self.length) + .field("bytes_read", &self.bytes_read) + .finish() + } +} + impl FixedLengthStream { pub fn wrap(stream: impl Stream>> + 'static, length: u64) -> Self { Self { diff --git a/worker/src/version.rs b/worker/src/version.rs index a194756b..e47ee96a 100644 --- a/worker/src/version.rs +++ b/worker/src/version.rs @@ -2,6 +2,7 @@ use crate::EnvBinding; use wasm_bindgen::{JsCast, JsValue}; use worker_sys::types::CfVersionMetadata; +#[derive(Debug)] pub struct WorkerVersionMetadata(CfVersionMetadata); unsafe impl Send for WorkerVersionMetadata {} diff --git a/worker/src/websocket.rs b/worker/src/websocket.rs index 86368baa..6be3594a 100644 --- a/worker/src/websocket.rs +++ b/worker/src/websocket.rs @@ -18,6 +18,7 @@ use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; pub use crate::ws_events::*; +pub use worker_sys::WebSocketRequestResponsePair; /// Struct holding the values for a JavaScript `WebSocketPair` #[derive(Debug, Clone, PartialEq, Eq)] @@ -207,7 +208,7 @@ impl WebSocket { /// Gets an implementation [`Stream`](futures::Stream) that yields events from the inner /// WebSocket. - pub fn events(&self) -> Result { + pub fn events(&self) -> Result> { let (tx, rx) = futures_channel::mpsc::unbounded::>(); let tx = Rc::new(tx); @@ -286,6 +287,7 @@ type EvCallback = Closure; /// }); /// ``` #[pin_project::pin_project(PinnedDrop)] +#[derive(Debug)] pub struct EventStream<'ws> { ws: &'ws WebSocket, #[pin] @@ -300,7 +302,7 @@ pub struct EventStream<'ws> { )>, } -impl<'ws> Stream for EventStream<'ws> { +impl Stream for EventStream<'_> { type Item = Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> {